summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan.moors@epfl.ch>2012-07-20 08:05:31 -0700
committerAdriaan Moors <adriaan.moors@epfl.ch>2012-07-20 08:05:31 -0700
commitc1816313f52860d47dc7f3efed195f5b0482743b (patch)
tree0d4e2dbd73b70d64cbef2cf262805dc2b4552a85
parentaca8c1efdb11ec598aa1b2bd94af33c0bb71c9a9 (diff)
parentb0c7f0b257d86634bb344405273200310f8ba386 (diff)
downloadscala-c1816313f52860d47dc7f3efed195f5b0482743b.tar.gz
scala-c1816313f52860d47dc7f3efed195f5b0482743b.tar.bz2
scala-c1816313f52860d47dc7f3efed195f5b0482743b.zip
Merge pull request #925 from VladUreche/issue/scaladoc
Fix a bunch of scaladoc issues: SI-3314 SI-4888 SI-5235 SI-5558 SI-4324 SI-5780 SI-4887 SI-3695 SI-4224 SI-4497 SI-5079 SI-6073 SI-5533 SI-5784
-rw-r--r--build.xml47
-rw-r--r--src/compiler/scala/tools/ant/Scaladoc.scala20
-rwxr-xr-xsrc/compiler/scala/tools/nsc/ast/DocComments.scala91
-rw-r--r--src/compiler/scala/tools/nsc/doc/DocFactory.scala9
-rw-r--r--src/compiler/scala/tools/nsc/doc/Settings.scala61
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala9
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala43
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/Page.scala22
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/page/Index.scala8
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/page/IndexScript.scala6
-rwxr-xr-xsrc/compiler/scala/tools/nsc/doc/html/page/ReferenceIndex.scala2
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/page/Template.scala162
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/page/diagram/DotDiagramGenerator.scala34
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/page/diagram/DotRunner.scala11
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/resource/lib/diagrams.css8
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js48
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/resource/lib/object_to_type_big.pngbin0 -> 9158 bytes
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css25
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js135
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/resource/lib/type.pngbin0 -> 3338 bytes
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/resource/lib/type_big.pngbin0 -> 7691 bytes
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/resource/lib/type_diagram.pngbin0 -> 3895 bytes
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/resource/lib/type_to_object_big.pngbin0 -> 9054 bytes
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/Entity.scala119
-rwxr-xr-xsrc/compiler/scala/tools/nsc/doc/model/IndexModelFactory.scala7
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/Links.scala24
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/MemberLookup.scala188
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala687
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala42
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/ModelFactoryTypeSupport.scala323
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/TypeEntity.scala4
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/comment/Body.scala7
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/comment/Comment.scala12
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala101
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/diagram/Diagram.scala28
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala58
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala3
-rwxr-xr-xsrc/compiler/scala/tools/nsc/util/DocStrings.scala6
-rw-r--r--src/library-aux/scala/AnyRef.scala1
-rw-r--r--src/library/scala/collection/immutable/List.scala3
-rw-r--r--src/library/scala/collection/immutable/StringOps.scala2
-rw-r--r--src/library/scala/collection/mutable/ArrayOps.scala2
-rw-r--r--src/partest/scala/tools/partest/ScaladocModelTest.scala67
-rw-r--r--test/scaladoc/resources/SI-3314-diagrams.scala78
-rw-r--r--test/scaladoc/resources/SI-3314.scala85
-rw-r--r--test/scaladoc/resources/SI-4360.scala42
-rw-r--r--test/scaladoc/resources/SI-5558.scala6
-rw-r--r--test/scaladoc/resources/SI-5784.scala28
-rw-r--r--test/scaladoc/resources/SI_4676.scala4
-rw-r--r--test/scaladoc/resources/Trac3484.scala27
-rw-r--r--test/scaladoc/resources/doc-root/AnyRef.scala1
-rw-r--r--test/scaladoc/resources/implicits-known-type-classes-res.scala38
-rw-r--r--test/scaladoc/resources/implicits-scopes-res.scala2
-rw-r--r--test/scaladoc/resources/links.scala57
-rw-r--r--test/scaladoc/resources/package-object-res.scala2
-rw-r--r--test/scaladoc/run/SI-3314-diagrams.check1
-rw-r--r--test/scaladoc/run/SI-3314-diagrams.scala28
-rw-r--r--test/scaladoc/run/SI-3314.check1
-rw-r--r--test/scaladoc/run/SI-3314.scala91
-rw-r--r--test/scaladoc/run/SI-3448.check1
-rw-r--r--test/scaladoc/run/SI-3448.scala38
-rw-r--r--test/scaladoc/run/SI-3484.check1
-rw-r--r--test/scaladoc/run/SI-3484.scala52
-rw-r--r--test/scaladoc/run/SI-4324.check1
-rw-r--r--test/scaladoc/run/SI-4324.scala24
-rw-r--r--test/scaladoc/run/SI-4360.check1
-rw-r--r--test/scaladoc/run/SI-4360.scala48
-rw-r--r--test/scaladoc/run/SI-4676.check1
-rw-r--r--test/scaladoc/run/SI-4676.scala26
-rw-r--r--test/scaladoc/run/SI-4887.check1
-rw-r--r--test/scaladoc/run/SI-4887.scala46
-rw-r--r--test/scaladoc/run/SI-5235.check4
-rw-r--r--test/scaladoc/run/SI-5235.scala87
-rw-r--r--test/scaladoc/run/SI-5533.check1
-rw-r--r--test/scaladoc/run/SI-5533.scala39
-rw-r--r--test/scaladoc/run/SI-5784.check1
-rw-r--r--test/scaladoc/run/SI-5784.scala44
-rw-r--r--test/scaladoc/run/SI-5965.check1
-rw-r--r--test/scaladoc/run/SI-5965.scala24
-rw-r--r--test/scaladoc/run/diagrams-base.scala6
-rw-r--r--test/scaladoc/run/diagrams-determinism.scala20
-rw-r--r--test/scaladoc/run/diagrams-filtering.scala2
-rw-r--r--test/scaladoc/run/groups.check1
-rw-r--r--test/scaladoc/run/groups.scala119
-rw-r--r--test/scaladoc/run/implicits-ambiguating.scala2
-rw-r--r--test/scaladoc/run/implicits-known-type-classes.check1
-rw-r--r--test/scaladoc/run/implicits-known-type-classes.scala33
-rw-r--r--test/scaladoc/run/implicits-scopes.scala6
-rw-r--r--test/scaladoc/run/implicits-shadowing.scala2
-rw-r--r--test/scaladoc/run/implicits-var-exp.scala35
-rw-r--r--test/scaladoc/run/links.check1
-rw-r--r--test/scaladoc/run/links.scala28
-rw-r--r--test/scaladoc/scalacheck/CommentFactoryTest.scala16
-rw-r--r--test/scaladoc/scalacheck/HtmlFactoryTest.scala73
-rw-r--r--test/scaladoc/scalacheck/IndexTest.scala13
95 files changed, 2880 insertions, 835 deletions
diff --git a/build.xml b/build.xml
index 160a699ca2..1fba806071 100644
--- a/build.xml
+++ b/build.xml
@@ -2044,7 +2044,7 @@ BOOTSTRAPPING BUILD (STRAP)
LIBRARIES (Forkjoin, FJBG, ASM)
============================================================================ -->
-
+
<target name="libs.clean" depends="pack.clean, asm.clean">
<delete dir="${build-libs.dir}" includeemptydirs="yes" quiet="yes" failonerror="no"/>
</target>
@@ -2082,8 +2082,9 @@ DOCUMENTATION
<property name="scaladoc.url" value="https://github.com/scala/scala/tree/${scaladoc.git.commit}/src"/>
<echo message="Scaladoc will point to ${scaladoc.url} for source files."/>
- <!-- Unless set with -Dscaladoc.raw.output, it won't be activated -->
+ <!-- Unless set with -Dscaladoc.<...>, these won't be activated -->
<property name="scaladoc.raw.output" value="no"/>
+ <property name="scaladoc.no.prefixes" value="no"/>
</target>
<target name="docs.pre-lib" depends="docs.start">
@@ -2111,7 +2112,11 @@ DOCUMENTATION
classpathref="pack.classpath"
addparams="${scalac.args.all}"
docRootContent="${src.dir}/library/rootdoc.txt"
- implicits="on" diagrams="on" rawOutput="${scaladoc.raw.output}">
+ implicits="on"
+ diagrams="on"
+ groups="on"
+ rawOutput="${scaladoc.raw.output}"
+ noPrefixes="${scaladoc.no.prefixes}">
<src>
<files includes="${src.dir}/actors-migration"/>
<files includes="${src.dir}/actors"/>
@@ -2195,7 +2200,11 @@ DOCUMENTATION
srcdir="${src.dir}/compiler"
docRootContent="${src.dir}/compiler/rootdoc.txt"
addparams="${scalac.args.all}"
- implicits="on" diagrams="on" rawOutput="${scaladoc.raw.output}">
+ implicits="on"
+ diagrams="on"
+ groups="on"
+ rawOutput="${scaladoc.raw.output}"
+ noPrefixes="${scaladoc.no.prefixes}">
<include name="**/*.scala"/>
</scaladoc>
<touch file="${build-docs.dir}/compiler.complete" verbose="no"/>
@@ -2217,7 +2226,11 @@ DOCUMENTATION
classpathref="pack.classpath"
srcdir="${src.dir}/jline/src/main/java"
addparams="${scalac.args.all}"
- implicits="on" diagrams="on" rawOutput="${scaladoc.raw.output}">
+ implicits="on"
+ diagrams="on"
+ groups="on"
+ rawOutput="${scaladoc.raw.output}"
+ noPrefixes="${scaladoc.no.prefixes}">
<include name="**/*.scala"/>
<include name="**/*.java"/>
</scaladoc>
@@ -2241,7 +2254,11 @@ DOCUMENTATION
classpathref="pack.classpath"
srcdir="${src.dir}/scalap"
addparams="${scalac.args.all}"
- implicits="on" diagrams="on" rawOutput="${scaladoc.raw.output}">
+ implicits="on"
+ diagrams="on"
+ groups="on"
+ rawOutput="${scaladoc.raw.output}"
+ noPrefixes="${scaladoc.no.prefixes}">
<include name="**/*.scala"/>
</scaladoc>
<touch file="${build-docs.dir}/scalap.complete" verbose="no"/>
@@ -2263,7 +2280,11 @@ DOCUMENTATION
classpathref="pack.classpath"
srcdir="${src.dir}/partest"
addparams="${scalac.args.all}"
- implicits="on" diagrams="on" rawOutput="${scaladoc.raw.output}">
+ implicits="on"
+ diagrams="on"
+ groups="on"
+ rawOutput="${scaladoc.raw.output}"
+ noPrefixes="${scaladoc.no.prefixes}">
<include name="**/*.scala"/>
</scaladoc>
<touch file="${build-docs.dir}/partest.complete" verbose="no"/>
@@ -2285,7 +2306,11 @@ DOCUMENTATION
classpathref="pack.classpath"
srcdir="${src.dir}/continuations/plugin"
addparams="${scalac.args.all}"
- implicits="on" diagrams="on" rawOutput="${scaladoc.raw.output}">
+ implicits="on"
+ diagrams="on"
+ groups="on"
+ rawOutput="${scaladoc.raw.output}"
+ noPrefixes="${scaladoc.no.prefixes}">
<include name="**/*.scala"/>
</scaladoc>
<touch file="${build-docs.dir}/continuations-plugin.complete" verbose="no"/>
@@ -2307,7 +2332,11 @@ DOCUMENTATION
classpathref="pack.classpath"
srcdir="${src.dir}/actors-migration"
addparams="${scalac.args.all}"
- implicits="on" diagrams="on" rawOutput="${scaladoc.raw.output}">
+ implicits="on"
+ diagrams="on"
+ groups="on"
+ rawOutput="${scaladoc.raw.output}"
+ noPrefixes="${scaladoc.no.prefixes}">
<include name="**/*.scala"/>
</scaladoc>
<touch file="${build-docs.dir}/actors-migration.complete" verbose="no"/>
diff --git a/src/compiler/scala/tools/ant/Scaladoc.scala b/src/compiler/scala/tools/ant/Scaladoc.scala
index 2cada92c1e..6201501a71 100644
--- a/src/compiler/scala/tools/ant/Scaladoc.scala
+++ b/src/compiler/scala/tools/ant/Scaladoc.scala
@@ -153,6 +153,11 @@ class Scaladoc extends ScalaMatchingTask {
/** Instruct the scaladoc to produce textual ouput from html pages, for easy diff-ing */
private var docRawOutput: Boolean = false
+ /** Instruct the scaladoc not to generate prefixes */
+ private var docNoPrefixes: Boolean = false
+
+ /** Instruct the scaladoc tool to group similar functions together */
+ private var docGroups: Boolean = false
/*============================================================================*\
** Properties setters **
@@ -427,6 +432,16 @@ class Scaladoc extends ScalaMatchingTask {
def setRawOutput(input: String) =
docRawOutput = Flag.getBooleanValue(input, "rawOutput")
+ /** Set the `noPrefixes` bit to prevent Scaladoc from generating prefixes in
+ * front of types -- may lead to confusion, but significantly speeds up the generation.
+ * @param input One of the flags `yes/no` or `on/off`. Default if no/off. */
+ def setNoPrefixes(input: String) =
+ docNoPrefixes = Flag.getBooleanValue(input, "noPrefixes")
+
+ /** Instruct the scaladoc tool to group similar functions together */
+ def setGroups(input: String) =
+ docGroups = Flag.getBooleanValue(input, "groups")
+
/*============================================================================*\
** Properties getters **
\*============================================================================*/
@@ -625,6 +640,8 @@ class Scaladoc extends ScalaMatchingTask {
docSettings.docDiagrams.value = docDiagrams
docSettings.docDiagramsDebug.value = docDiagramsDebug
docSettings.docRawOutput.value = docRawOutput
+ docSettings.docNoPrefixes.value = docNoPrefixes
+ docSettings.docGroups.value = docGroups
if(!docDiagramsDotPath.isEmpty) docSettings.docDiagramsDotPath.value = docDiagramsDotPath.get
if (!docgenerator.isEmpty) docSettings.docgenerator.value = docgenerator.get
@@ -662,11 +679,10 @@ class Scaladoc extends ScalaMatchingTask {
exception.printStackTrace()
safeBuildError("Document failed because of an internal documenter error (" +
exception.getMessage + "); see the error output for details.")
- case exception =>
+ case exception : Throwable =>
exception.printStackTrace()
safeBuildError("Document failed because of an internal documenter error " +
"(no error message provided); see the error output for details.")
}
}
-
}
diff --git a/src/compiler/scala/tools/nsc/ast/DocComments.scala b/src/compiler/scala/tools/nsc/ast/DocComments.scala
index b545140c4a..c2530bd5c7 100755
--- a/src/compiler/scala/tools/nsc/ast/DocComments.scala
+++ b/src/compiler/scala/tools/nsc/ast/DocComments.scala
@@ -171,15 +171,15 @@ trait DocComments { self: Global =>
* 3. If there is no @return section in `dst` but there is one in `src`, copy it.
*/
def merge(src: String, dst: String, sym: Symbol, copyFirstPara: Boolean = false): String = {
- val srcSections = tagIndex(src)
- val dstSections = tagIndex(dst)
- val srcParams = paramDocs(src, "@param", srcSections)
- val dstParams = paramDocs(dst, "@param", dstSections)
- val srcTParams = paramDocs(src, "@tparam", srcSections)
- val dstTParams = paramDocs(dst, "@tparam", dstSections)
- val out = new StringBuilder
- var copied = 0
- var tocopy = startTag(dst, dstSections dropWhile (!isMovable(dst, _)))
+ val srcSections = tagIndex(src)
+ val dstSections = tagIndex(dst)
+ val srcParams = paramDocs(src, "@param", srcSections)
+ val dstParams = paramDocs(dst, "@param", dstSections)
+ val srcTParams = paramDocs(src, "@tparam", srcSections)
+ val dstTParams = paramDocs(dst, "@tparam", dstSections)
+ val out = new StringBuilder
+ var copied = 0
+ var tocopy = startTag(dst, dstSections dropWhile (!isMovable(dst, _)))
if (copyFirstPara) {
val eop = // end of comment body (first para), which is delimited by blank line, or tag, or end of comment
@@ -209,6 +209,11 @@ trait DocComments { self: Global =>
for (tparam <- sym.typeParams)
mergeSection(srcTParams get tparam.name.toString, dstTParams get tparam.name.toString)
mergeSection(returnDoc(src, srcSections), returnDoc(dst, dstSections))
+ if (sym.name.toString == "isEmpty") {
+ println(groupDoc(src, srcSections))
+ println(groupDoc(dst, dstSections))
+ }
+ mergeSection(groupDoc(src, srcSections), groupDoc(dst, dstSections))
if (out.length == 0) dst
else {
@@ -457,22 +462,16 @@ trait DocComments { self: Global =>
case List() => NoType
case site :: sites1 => select(site.thisType, name, findIn(sites1))
}
- val (classes, pkgs) = site.ownerChain.span(!_.isPackageClass)
- findIn(classes ::: List(pkgs.head, rootMirror.RootClass))
+ // Previously, searching was taking place *only* in the current package and in the root package
+ // now we're looking for it everywhere in the hierarchy, so we'll be able to link variable expansions like
+ // immutable.Seq in package immutable
+ //val (classes, pkgs) = site.ownerChain.span(!_.isPackageClass)
+ //val sites = (classes ::: List(pkgs.head, rootMirror.RootClass)))
+ //findIn(sites)
+ findIn(site.ownerChain ::: List(definitions.EmptyPackage))
}
- def getType(_str: String, variable: String): Type = {
- /*
- * work around the backticks issue suggested by Simon in
- * https://groups.google.com/forum/?hl=en&fromgroups#!topic/scala-internals/z7s1CCRCz74
- * ideally, we'd have a removeWikiSyntax method in the CommentFactory to completely eliminate the wiki markup
- */
- val str =
- if (_str.length >= 2 && _str.startsWith("`") && _str.endsWith("`"))
- _str.substring(1, _str.length - 2)
- else
- _str
-
+ def getType(str: String, variable: String): Type = {
def getParts(start: Int): List[String] = {
val end = skipIdent(str, start)
if (end == start) List()
@@ -484,7 +483,7 @@ trait DocComments { self: Global =>
val parts = getParts(0)
if (parts.isEmpty) {
reporter.error(comment.codePos, "Incorrect variable expansion for " + variable + " in use case. Does the " +
- "variable expand to wiki syntax when documenting " + site + "?")
+ "variable expand to wiki syntax when documenting " + site + "?")
return ErrorType
}
val partnames = (parts.init map newTermName) :+ newTypeName(parts.last)
@@ -498,25 +497,46 @@ trait DocComments { self: Global =>
case _ =>
(getSite(partnames.head), partnames.tail)
}
- (start /: rest)(select(_, _, NoType))
+ val result = (start /: rest)(select(_, _, NoType))
+ if (result == NoType)
+ reporter.warning(comment.codePos, "Could not find the type " + variable + " points to while expanding it " +
+ "for the usecase signature of " + sym + " in " + site + "." +
+ "In this context, " + variable + " = \"" + str + "\".")
+ result
+ }
+
+ /**
+ * work around the backticks issue suggested by Simon in
+ * https://groups.google.com/forum/?hl=en&fromgroups#!topic/scala-internals/z7s1CCRCz74
+ * ideally, we'd have a removeWikiSyntax method in the CommentFactory to completely eliminate the wiki markup
+ */
+ def cleanupVariable(str: String) = {
+ val tstr = str.trim
+ if (tstr.length >= 2 && tstr.startsWith("`") && tstr.endsWith("`"))
+ tstr.substring(1, tstr.length - 1)
+ else
+ tstr
}
- val aliasExpansions: List[Type] =
+ // the Boolean tells us whether we can normalize: if we found an actual type, then yes, we can normalize, else no,
+ // use the synthetic alias created for the variable
+ val aliasExpansions: List[(Type, Boolean)] =
for (alias <- aliases) yield
lookupVariable(alias.name.toString.substring(1), site) match {
case Some(repl) =>
- val tpe = getType(repl.trim, alias.name.toString)
- if (tpe != NoType) tpe
+ val repl2 = cleanupVariable(repl)
+ val tpe = getType(repl2, alias.name.toString)
+ if (tpe != NoType) (tpe, true)
else {
- val alias1 = alias.cloneSymbol(rootMirror.RootClass, alias.rawflags, newTypeName(repl))
- typeRef(NoPrefix, alias1, Nil)
+ val alias1 = alias.cloneSymbol(rootMirror.RootClass, alias.rawflags, newTypeName(repl2))
+ (typeRef(NoPrefix, alias1, Nil), false)
}
case None =>
- typeRef(NoPrefix, alias, Nil)
+ (typeRef(NoPrefix, alias, Nil), false)
}
- def subst(sym: Symbol, from: List[Symbol], to: List[Type]): Type =
- if (from.isEmpty) sym.tpe
+ def subst(sym: Symbol, from: List[Symbol], to: List[(Type, Boolean)]): (Type, Boolean) =
+ if (from.isEmpty) (sym.tpe, false)
else if (from.head == sym) to.head
else subst(sym, from.tail, to.tail)
@@ -524,8 +544,9 @@ trait DocComments { self: Global =>
def apply(tp: Type) = mapOver(tp) match {
case tp1 @ TypeRef(pre, sym, args) if (sym.name.length > 1 && sym.name.startChar == '$') =>
subst(sym, aliases, aliasExpansions) match {
- case TypeRef(pre1, sym1, _) =>
- typeRef(pre1, sym1, args)
+ case (TypeRef(pre1, sym1, _), canNormalize) =>
+ val tpe = typeRef(pre1, sym1, args)
+ if (canNormalize) tpe.normalize else tpe
case _ =>
tp1
}
diff --git a/src/compiler/scala/tools/nsc/doc/DocFactory.scala b/src/compiler/scala/tools/nsc/doc/DocFactory.scala
index 3c92c3b4b6..27a03d5381 100644
--- a/src/compiler/scala/tools/nsc/doc/DocFactory.scala
+++ b/src/compiler/scala/tools/nsc/doc/DocFactory.scala
@@ -48,8 +48,8 @@ class DocFactory(val reporter: Reporter, val settings: doc.Settings) { processor
/** Creates a scaladoc site for all symbols defined in this call's `source`,
* as well as those defined in `sources` of previous calls to the same processor.
- * @param files The list of paths (relative to the compiler's source path,
- * or absolute) of files to document. */
+ * @param source The list of paths (relative to the compiler's source path,
+ * or absolute) of files to document or the source code. */
def makeUniverse(source: Either[List[String], String]): Option[Universe] = {
assert(settings.docformat.value == "html")
source match {
@@ -81,9 +81,11 @@ class DocFactory(val reporter: Reporter, val settings: doc.Settings) { processor
new { override val global: compiler.type = compiler }
with model.ModelFactory(compiler, settings)
with model.ModelFactoryImplicitSupport
+ with model.ModelFactoryTypeSupport
with model.diagram.DiagramFactory
with model.comment.CommentFactory
- with model.TreeFactory {
+ with model.TreeFactory
+ with model.MemberLookup {
override def templateShouldDocument(sym: compiler.Symbol, inTpl: TemplateImpl) =
extraTemplatesToDocument(sym) || super.templateShouldDocument(sym, inTpl)
}
@@ -99,7 +101,6 @@ class DocFactory(val reporter: Reporter, val settings: doc.Settings) { processor
println("no documentable class found in compilation units")
None
}
-
}
object NoCompilerRunException extends ControlThrowable { }
diff --git a/src/compiler/scala/tools/nsc/doc/Settings.scala b/src/compiler/scala/tools/nsc/doc/Settings.scala
index 31e49131f6..ad178b8158 100644
--- a/src/compiler/scala/tools/nsc/doc/Settings.scala
+++ b/src/compiler/scala/tools/nsc/doc/Settings.scala
@@ -147,7 +147,7 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_))
val docDiagramsDotTimeout = IntSetting(
"-diagrams-dot-timeout",
- "The timeout before the graphviz dot util is forecefully closed, in seconds (default: 10)",
+ "The timeout before the graphviz dot util is forcefully closed, in seconds (default: 10)",
10,
None,
_ => None
@@ -166,6 +166,33 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_))
"For each html file, create another .html.raw file containing only the text. (can be used for quickly diffing two scaladoc outputs)"
)
+ val docNoPrefixes = BooleanSetting (
+ "-no-prefixes",
+ "Prevents generating prefixes in types, possibly creating ambiguous references, but significantly speeding up scaladoc."
+ )
+
+ val docNoLinkWarnings = BooleanSetting (
+ "-no-link-warnings",
+ "Avoid warnings for ambiguous and incorrect links."
+ )
+
+ val docSkipPackages = StringSetting (
+ "-skip-packages",
+ "<package1>:...:<packageN>",
+ "A colon-delimited list of fully qualified package names that will be skipped from scaladoc.",
+ ""
+ )
+
+ val docExpandAllTypes = BooleanSetting (
+ "-expand-all-types",
+ "Expand all type aliases and abstract types into full template pages. (locally this can be done with the @template annotation)"
+ )
+
+ val docGroups = BooleanSetting (
+ "-groups",
+ "Group similar functions together (based on the @group annotation)"
+ )
+
// Somewhere slightly before r18708 scaladoc stopped building unless the
// self-type check was suppressed. I hijacked the slotted-for-removal-anyway
// suppress-vt-warnings option and renamed it for this purpose.
@@ -177,7 +204,9 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_))
docDiagrams, docDiagramsDebug, docDiagramsDotPath,
docDiagramsDotTimeout, docDiagramsDotRestart,
docImplicits, docImplicitsDebug, docImplicitsShowAll,
- docDiagramsMaxNormalClasses, docDiagramsMaxImplicitClasses
+ docDiagramsMaxNormalClasses, docDiagramsMaxImplicitClasses,
+ docNoPrefixes, docNoLinkWarnings, docRawOutput, docSkipPackages,
+ docExpandAllTypes, docGroups
)
val isScaladocSpecific: String => Boolean = scaladocSpecific map (_.name)
@@ -186,6 +215,15 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_))
// set by the testsuite, when checking test output
var scaladocQuietRun = false
+ lazy val skipPackageNames =
+ if (docSkipPackages.value == "")
+ Set[String]()
+ else
+ docSkipPackages.value.toLowerCase.split(':').toSet
+
+ def skipPackage(qname: String) =
+ skipPackageNames(qname.toLowerCase)
+
/**
* 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. :)
@@ -198,16 +236,15 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_))
* the function result should be a humanly-understandable description of the type class
*/
val knownTypeClasses: Map[String, String => String] = Map() +
- // TODO: Bring up to date and test these
- ("scala.package.Numeric" -> ((tparam: String) => tparam + " is a numeric class, such as Int, Long, Float or Double")) +
- ("scala.package.Integral" -> ((tparam: String) => tparam + " is an integral numeric class, such as Int or Long")) +
- ("scala.package.Fractional" -> ((tparam: String) => tparam + " is a fractional numeric class, such as Float or Double")) +
- ("scala.reflect.Manifest" -> ((tparam: String) => tparam + " is accompanied by a Manifest, which is a runtime representation of its type that survives erasure")) +
- ("scala.reflect.ClassManifest" -> ((tparam: String) => tparam + " is accompanied by a ClassManifest, which is a runtime representation of its type that survives erasure")) +
- ("scala.reflect.OptManifest" -> ((tparam: String) => tparam + " is accompanied by an OptManifest, which can be either a runtime representation of its type or the NoManifest, which means the runtime type is not available")) +
- ("scala.reflect.ClassTag" -> ((tparam: String) => tparam + " is accompanied by a ClassTag, which is a runtime representation of its type that survives erasure")) +
- ("scala.reflect.AbsTypeTag" -> ((tparam: String) => tparam + " is accompanied by an AbsTypeTag, which is a runtime representation of its type that survives erasure")) +
- ("scala.reflect.TypeTag" -> ((tparam: String) => tparam + " is accompanied by a TypeTag, which is a runtime representation of its type that survives erasure"))
+ ("scala.math.Numeric" -> ((tparam: String) => tparam + " is a numeric class, such as Int, Long, Float or Double")) +
+ ("scala.math.Integral" -> ((tparam: String) => tparam + " is an integral numeric class, such as Int or Long")) +
+ ("scala.math.Fractional" -> ((tparam: String) => tparam + " is a fractional numeric class, such as Float or Double")) +
+ ("scala.reflect.Manifest" -> ((tparam: String) => tparam + " is accompanied by a Manifest, which is a runtime representation of its type that survives erasure")) +
+ ("scala.reflect.ClassManifest" -> ((tparam: String) => tparam + " is accompanied by a ClassManifest, which is a runtime representation of its type that survives erasure")) +
+ ("scala.reflect.OptManifest" -> ((tparam: String) => tparam + " is accompanied by an OptManifest, which can be either a runtime representation of its type or the NoManifest, which means the runtime type is not available")) +
+ ("scala.reflect.ClassTag" -> ((tparam: String) => tparam + " is accompanied by a ClassTag, which is a runtime representation of its type that survives erasure")) +
+ ("scala.reflect.AbsTypeTag" -> ((tparam: String) => tparam + " is accompanied by an AbsTypeTag, which is a runtime representation of its type that survives erasure")) +
+ ("scala.reflect.base.TypeTags.TypeTag" -> ((tparam: String) => tparam + " is accompanied by a TypeTag, which is a runtime representation of its type that survives erasure"))
/**
* Set of classes to exclude from index and diagrams
diff --git a/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala b/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala
index 51c5793d46..436425df83 100644
--- a/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala
+++ b/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala
@@ -53,11 +53,16 @@ class HtmlFactory(val universe: doc.Universe, index: doc.Index) {
"trait.png",
"trait_big.png",
"trait_diagram.png",
+ "type.png",
+ "type_big.png",
+ "type_diagram.png",
"class_to_object_big.png",
"object_to_class_big.png",
- "object_to_trait_big.png",
"trait_to_object_big.png",
+ "object_to_trait_big.png",
+ "type_to_object_big.png",
+ "object_to_type_big.png",
"arrow-down.png",
"arrow-right.png",
@@ -138,7 +143,7 @@ class HtmlFactory(val universe: doc.Universe, index: doc.Index) {
if (!(written contains tpl)) {
writeForThis(new page.Template(universe, diagramGenerator, tpl))
written += tpl
- tpl.templates map writeTemplate
+ tpl.templates collect { case d: DocTemplateEntity => d } map writeTemplate
}
}
diff --git a/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala b/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala
index 4a1a8cf898..aa2df57967 100644
--- a/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala
+++ b/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala
@@ -122,17 +122,30 @@ abstract class HtmlPage extends Page { thisPage =>
case Text(text) => xml.Text(text)
case Summary(in) => inlineToHtml(in)
case HtmlTag(tag) => xml.Unparsed(tag)
- case EntityLink(target, template) => template() match {
- case Some(tpl) =>
- templateToHtml(tpl)
- case None =>
- xml.Text(target)
- }
+ case EntityLink(target, link) => linkToHtml(target, link, true)
+ }
+
+ def linkToHtml(text: Inline, link: LinkTo, hasLinks: Boolean) = link match {
+ case LinkToTpl(dtpl) =>
+ if (hasLinks)
+ <a href={ relativeLinkTo(dtpl) } class="extype" name={ dtpl.qualifiedName }>{ inlineToHtml(text) }</a>
+ else
+ <span class="extype" name={ dtpl.qualifiedName }>{ inlineToHtml(text) }</span>
+ case LinkToMember(mbr, inTpl) =>
+ if (hasLinks)
+ <a href={ relativeLinkTo(inTpl) + "#" + mbr.signature } class="extmbr" name={ mbr.qualifiedName }>{ inlineToHtml(text) }</a>
+ else
+ <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 NoLink =>
+ inlineToHtml(text)
}
def typeToHtml(tpes: List[model.TypeEntity], hasLinks: Boolean): NodeSeq = tpes match {
case Nil =>
- sys.error("Internal Scaladoc error")
+ NodeSeq.Empty
case List(tpe) =>
typeToHtml(tpe, hasLinks)
case tpe :: rest =>
@@ -153,15 +166,9 @@ abstract class HtmlPage extends Page { thisPage =>
}
}
def toLinksIn(inPos: Int, starts: List[Int]): NodeSeq = {
- val (tpl, width) = tpe.refEntity(inPos)
- (tpl match {
- case dtpl:DocTemplateEntity if hasLinks =>
- <a href={ relativeLinkTo(dtpl) } class="extype" name={ dtpl.qualifiedName }>{
- string.slice(inPos, inPos + width)
- }</a>
- case tpl =>
- <span class="extype" name={ tpl.qualifiedName }>{ string.slice(inPos, inPos + width) }</span>
- }) ++ toLinksOut(inPos + width, starts.tail)
+ val (link, width) = tpe.refEntity(inPos)
+ val text = comment.Text(string.slice(inPos, inPos + width))
+ linkToHtml(text, link, hasLinks) ++ toLinksOut(inPos + width, starts.tail)
}
if (hasLinks)
toLinksOut(0, tpe.refEntity.keySet.toList)
@@ -204,10 +211,12 @@ abstract class HtmlPage extends Page { thisPage =>
else if (ety.isTrait) "trait_big.png"
else if (ety.isClass && !ety.companion.isEmpty && ety.companion.get.visibility.isPublic && ety.companion.get.inSource != None) "class_to_object_big.png"
else if (ety.isClass) "class_big.png"
+ else if ((ety.isAbstractType || ety.isAliasType) && !ety.companion.isEmpty && ety.companion.get.visibility.isPublic && ety.companion.get.inSource != None) "type_to_object_big.png"
+ else if ((ety.isAbstractType || ety.isAliasType)) "type_big.png"
else if (ety.isObject && !ety.companion.isEmpty && ety.companion.get.visibility.isPublic && ety.companion.get.inSource != None && ety.companion.get.isClass) "object_to_class_big.png"
else if (ety.isObject && !ety.companion.isEmpty && ety.companion.get.visibility.isPublic && ety.companion.get.inSource != None && ety.companion.get.isTrait) "object_to_trait_big.png"
+ else if (ety.isObject && !ety.companion.isEmpty && ety.companion.get.visibility.isPublic && ety.companion.get.inSource != None && (ety.companion.get.isAbstractType || ety.companion.get.isAliasType)) "object_to_trait_big.png"
else if (ety.isObject) "object_big.png"
else if (ety.isPackage) "package_big.png"
else "class_big.png" // FIXME: an entity *should* fall into one of the above categories, but AnyRef is somehow not
-
}
diff --git a/src/compiler/scala/tools/nsc/doc/html/Page.scala b/src/compiler/scala/tools/nsc/doc/html/Page.scala
index 5e3ab87ccd..d30ca5dc08 100644
--- a/src/compiler/scala/tools/nsc/doc/html/Page.scala
+++ b/src/compiler/scala/tools/nsc/doc/html/Page.scala
@@ -48,13 +48,21 @@ abstract class Page {
* @param generator The generator that is writing this page. */
def writeFor(site: HtmlFactory): Unit
- def docEntityKindToString(ety: DocTemplateEntity) =
- if (ety.isTrait) "trait"
- else if (ety.isCaseClass) "case class"
- else if (ety.isClass) "class"
- else if (ety.isObject) "object"
- else if (ety.isPackage) "package"
- else "class" // FIXME: an entity *should* fall into one of the above categories, but AnyRef is somehow not
+ def kindToString(mbr: MemberEntity) =
+ mbr match {
+ case c: Class => if (c.isCaseClass) "case class" else "class"
+ case _: Trait => "trait"
+ case _: Package => "package"
+ case _: Object => "object"
+ case _: AbstractType => "type"
+ case _: AliasType => "type"
+ case _: Constructor => "new"
+ case v: Def => "def"
+ case v: Val if (v.isLazyVal) => "lazy val"
+ case v: Val if (v.isVal) => "val"
+ case v: Val if (v.isVar) => "var"
+ case _ => sys.error("Cannot create kind for: " + mbr + " of class " + mbr.getClass)
+ }
def templateToPath(tpl: TemplateEntity): List[String] = {
def doName(tpl: TemplateEntity): String =
diff --git a/src/compiler/scala/tools/nsc/doc/html/page/Index.scala b/src/compiler/scala/tools/nsc/doc/html/page/Index.scala
index 0e894a03bf..b15d602b57 100644
--- a/src/compiler/scala/tools/nsc/doc/html/page/Index.scala
+++ b/src/compiler/scala/tools/nsc/doc/html/page/Index.scala
@@ -14,7 +14,7 @@ import scala.collection._
import scala.xml._
import scala.util.parsing.json.{JSONObject, JSONArray}
-class Index(universe: doc.Universe, index: doc.Index) extends HtmlPage {
+class Index(universe: doc.Universe, val index: doc.Index) extends HtmlPage {
def path = List("index.html")
@@ -61,12 +61,14 @@ class Index(universe: doc.Universe, index: doc.Index) extends HtmlPage {
}
<ol class="templates">{
val tpls: Map[String, Seq[DocTemplateEntity]] =
- (pack.templates filter (t => !t.isPackage && !universe.settings.hardcoded.isExcluded(t.qualifiedName) )) groupBy (_.name)
+ (pack.templates collect {
+ case t: DocTemplateEntity if !t.isPackage && !universe.settings.hardcoded.isExcluded(t.qualifiedName) => t
+ }) groupBy (_.name)
val placeholderSeq: NodeSeq = <div class="placeholder"></div>
def createLink(entity: DocTemplateEntity, includePlaceholder: Boolean, includeText: Boolean) = {
- val entityType = docEntityKindToString(entity)
+ val entityType = kindToString(entity)
val linkContent = (
{ if (includePlaceholder) placeholderSeq else NodeSeq.Empty }
++
diff --git a/src/compiler/scala/tools/nsc/doc/html/page/IndexScript.scala b/src/compiler/scala/tools/nsc/doc/html/page/IndexScript.scala
index 2b68ac2937..a37dd3fb8b 100644
--- a/src/compiler/scala/tools/nsc/doc/html/page/IndexScript.scala
+++ b/src/compiler/scala/tools/nsc/doc/html/page/IndexScript.scala
@@ -27,7 +27,7 @@ class IndexScript(universe: doc.Universe, index: doc.Index) extends Page {
val ary = merged.keys.toList.sortBy(_.toLowerCase).map(key => {
val pairs = merged(key).map(
- t => docEntityKindToString(t) -> relativeLinkTo(t)
+ t => kindToString(t) -> relativeLinkTo(t)
) :+ ("name" -> key)
JSONObject(scala.collection.immutable.Map(pairs : _*))
@@ -62,7 +62,9 @@ class IndexScript(universe: doc.Universe, index: doc.Index) extends Page {
def allPackagesWithTemplates = {
Map(allPackages.map((key) => {
- key -> key.templates.filter(t => !t.isPackage && !universe.settings.hardcoded.isExcluded(t.qualifiedName))
+ key -> key.templates.collect {
+ case t: DocTemplateEntity if !t.isPackage && !universe.settings.hardcoded.isExcluded(t.qualifiedName) => t
+ }
}) : _*)
}
}
diff --git a/src/compiler/scala/tools/nsc/doc/html/page/ReferenceIndex.scala b/src/compiler/scala/tools/nsc/doc/html/page/ReferenceIndex.scala
index a76cc231b4..effaee711d 100755
--- a/src/compiler/scala/tools/nsc/doc/html/page/ReferenceIndex.scala
+++ b/src/compiler/scala/tools/nsc/doc/html/page/ReferenceIndex.scala
@@ -34,7 +34,7 @@ class ReferenceIndex(letter: Char, index: doc.Index, universe: Universe) extends
} else {
html
}
- })
+ }).toList.distinct
<div class="entry">
<div class="name">{
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 0d0410c7e2..422ea5ef1c 100644
--- a/src/compiler/scala/tools/nsc/doc/html/page/Template.scala
+++ b/src/compiler/scala/tools/nsc/doc/html/page/Template.scala
@@ -64,10 +64,10 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp
nonAbsValueMembers partition (_.deprecation.isDefined)
val (concValueMembers, shadowedImplicitMembers) =
- nonDeprValueMembers partition (!Template.isShadowedOrAmbiguousImplicit(_))
+ nonDeprValueMembers partition (!_.isShadowedOrAmbiguousImplicit)
val typeMembers =
- tpl.abstractTypes ++ tpl.aliasTypes ++ tpl.templates.filter(x => x.isTrait || x.isClass) sorted
+ tpl.abstractTypes ++ tpl.aliasTypes ++ tpl.templates.filter(x => x.isTrait || x.isClass) sorted (implicitly[Ordering[MemberEntity]])
val constructors = (tpl match {
case cls: Class => (cls.constructors: List[MemberEntity]).sorted
@@ -92,7 +92,7 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp
<p id="owner">{ templatesToHtml(tpl.inTemplate.toRoot.reverse.tail, xml.Text(".")) }</p>
}
- <body class={ if (tpl.isTrait || tpl.isClass || tpl.qualifiedName == "scala.AnyRef") "type" else "value" }>
+ <body class={ if (tpl.isType) "type" else "value" }>
<div id="definition">
{
tpl.companion match {
@@ -110,10 +110,26 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp
<div id="mbrsel">
<div id='textfilter'><span class='pre'/><span class='input'><input id='mbrsel-input' type='text' accesskey='/'/></span><span class='post'/></div>
- { if (tpl.linearizationTemplates.isEmpty && tpl.conversions.isEmpty) NodeSeq.Empty else
+ { if (tpl.linearizationTemplates.isEmpty && tpl.conversions.isEmpty && (!universe.settings.docGroups.value || (tpl.members.map(_.group).distinct.length == 1)))
+ NodeSeq.Empty
+ else
<div id="order">
<span class="filtertype">Ordering</span>
- <ol><li class="alpha in"><span>Alphabetic</span></li><li class="inherit out"><span>By inheritance</span></li></ol>
+ <ol>
+ {
+ if (!universe.settings.docGroups.value || (tpl.members.map(_.group).distinct.length == 1))
+ NodeSeq.Empty
+ else
+ <li class="group out"><span>Grouped</span></li>
+ }
+ <li class="alpha in"><span>Alphabetic</span></li>
+ {
+ if (tpl.linearizationTemplates.isEmpty && tpl.conversions.isEmpty)
+ NodeSeq.Empty
+ else
+ <li class="inherit out"><span>By inheritance</span></li>
+ }
+ </ol>
</div>
}
{ if (tpl.linearizationTemplates.isEmpty && tpl.conversions.isEmpty) NodeSeq.Empty else
@@ -223,6 +239,25 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp
}
</div>
+ <div id="groupedMembers">
+ {
+ val allGroups = tpl.members.map(_.group).distinct
+ val orderedGroups = allGroups.map(group => (tpl.groupPriority(group), group)).sorted.map(_._2)
+ // linearization
+ NodeSeq fromSeq (for (group <- orderedGroups) yield
+ <div class="group" name={ group }>
+ <h3>{ tpl.groupName(group) }</h3>
+ {
+ tpl.groupDescription(group) match {
+ case Some(body) => <div class="comment cmt">{ bodyToHtml(body) }</div>
+ case _ => NodeSeq.Empty
+ }
+ }
+ </div>
+ )
+ }
+ </div>
+
</div>
<div id="tooltip" ></div>
@@ -238,44 +273,13 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp
</body>
}
- def boundsToString(hi: Option[TypeEntity], lo: Option[TypeEntity]): String = {
- def bound0(bnd: Option[TypeEntity], pre: String): String = bnd match {
- case None => ""
- case Some(tpe) => pre ++ tpe.toString
- }
- bound0(hi, "<:") ++ bound0(lo, ">:")
- }
-
- def tparamsToString(tpss: List[TypeParam]): String = {
- if (tpss.isEmpty) "" else {
- def tparam0(tp: TypeParam): String =
- tp.variance + tp.name + boundsToString(tp.hi, tp.lo)
- def tparams0(tpss: List[TypeParam]): String = (tpss: @unchecked) match {
- case tp :: Nil => tparam0(tp)
- case tp :: tps => tparam0(tp) ++ ", " ++ tparams0(tps)
- }
- "[" + tparams0(tpss) + "]"
- }
- }
-
- def defParamsToString(d: MemberEntity with Def): String = {
- val paramLists: List[String] =
- if (d.valueParams.isEmpty) Nil
- else d.valueParams map (ps => ps map (_.resultType.name) mkString ("(",",",")"))
-
- tparamsToString(d.typeParams) + paramLists.mkString
- }
-
def memberToHtml(mbr: MemberEntity, inTpl: DocTemplateEntity): NodeSeq = {
- val defParamsString = mbr match {
- case d:MemberEntity with Def => defParamsToString(d)
- case _ => ""
- }
val memberComment = memberToCommentHtml(mbr, inTpl, false)
<li name={ mbr.definitionName } visbl={ if (mbr.visibility.isProtected) "prt" else "pub" }
data-isabs={ mbr.isAbstract.toString }
- fullComment={ if(memberComment.filter(_.label=="div").isEmpty) "no" else "yes" }>
- <a id={ mbr.name +defParamsString +":"+ mbr.resultType.name}/>
+ fullComment={ if(memberComment.filter(_.label=="div").isEmpty) "no" else "yes" }
+ group={ mbr.group }>
+ <a id={ mbr.signature }/>
{ signature(mbr, false) }
{ memberComment }
</li>
@@ -419,7 +423,7 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp
{ constraintText }
</dd>
} ++ {
- if (Template.isShadowedOrAmbiguousImplicit(mbr)) {
+ if (mbr.isShadowedOrAmbiguousImplicit) {
// These are the members that are shadowing or ambiguating the current implicit
// see ImplicitMemberShadowing trait for more information
val shadowingSuggestion = {
@@ -434,10 +438,10 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp
}
val shadowingWarning: NodeSeq =
- if (Template.isShadowedImplicit(mbr))
+ if (mbr.isShadowedImplicit)
xml.Text("This implicitly inherited member is shadowed by one or more members in this " +
"class.") ++ shadowingSuggestion
- else if (Template.isAmbiguousImplicit(mbr))
+ else if (mbr.isAmbiguousImplicit)
xml.Text("This implicitly inherited member is ambiguous. One or more implicitly " +
"inherited members have similar signatures, so calling this member may produce an ambiguous " +
"implicit conversion compiler error.") ++ shadowingSuggestion
@@ -633,13 +637,13 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp
}
val typeHierarchy = if (s.docDiagrams.isSetByUser) mbr match {
- case dtpl: DocTemplateEntity if isSelf && !isReduced && dtpl.inheritanceDiagram.isDefined =>
+ case dtpl: DocTemplateEntity if isSelf && !isReduced =>
makeDiagramHtml(dtpl, dtpl.inheritanceDiagram, "Type Hierarchy", "inheritance-diagram")
case _ => NodeSeq.Empty
} else NodeSeq.Empty // diagrams not generated
val contentHierarchy = if (s.docDiagrams.isSetByUser) mbr match {
- case dtpl: DocTemplateEntity if isSelf && !isReduced && dtpl.contentDiagram.isDefined =>
+ case dtpl: DocTemplateEntity if isSelf && !isReduced =>
makeDiagramHtml(dtpl, dtpl.contentDiagram, "Content Hierarchy", "content-diagram")
case _ => NodeSeq.Empty
} else NodeSeq.Empty // diagrams not generated
@@ -647,19 +651,6 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp
memberComment ++ paramComments ++ attributesBlock ++ linearization ++ subclasses ++ typeHierarchy ++ contentHierarchy
}
- def kindToString(mbr: MemberEntity): String = {
- mbr match {
- case tpl: DocTemplateEntity => docEntityKindToString(tpl)
- case ctor: Constructor => "new"
- case tme: MemberEntity =>
- ( if (tme.isDef) "def"
- else if (tme.isVal) "val"
- else if (tme.isLazyVal) "lazy val"
- else if (tme.isVar) "var"
- else "type")
- }
- }
-
def boundsToHtml(hi: Option[TypeEntity], lo: Option[TypeEntity], hasLinks: Boolean): NodeSeq = {
def bound0(bnd: Option[TypeEntity], pre: String): NodeSeq = bnd match {
case None => NodeSeq.Empty
@@ -677,13 +668,13 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp
case PrivateInTemplate(owner) if (owner == mbr.inTemplate) =>
Some(Paragraph(CText("private")))
case PrivateInTemplate(owner) =>
- Some(Paragraph(Chain(List(CText("private["), EntityLink(owner.qualifiedName, () => Some(owner)), CText("]")))))
+ Some(Paragraph(Chain(List(CText("private["), EntityLink(comment.Text(owner.qualifiedName), LinkToTpl(owner)), CText("]")))))
case ProtectedInInstance() =>
Some(Paragraph(CText("protected[this]")))
case ProtectedInTemplate(owner) if (owner == mbr.inTemplate) =>
Some(Paragraph(CText("protected")))
case ProtectedInTemplate(owner) =>
- Some(Paragraph(Chain(List(CText("protected["), EntityLink(owner.qualifiedName, () => Some(owner)), CText("]")))))
+ Some(Paragraph(Chain(List(CText("protected["), EntityLink(comment.Text(owner.qualifiedName), LinkToTpl(owner)), CText("]")))))
case Public() =>
None
}
@@ -700,8 +691,8 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp
<span class="symbol">
{
val nameClass =
- if (Template.isImplicit(mbr))
- if (Template.isShadowedOrAmbiguousImplicit(mbr))
+ if (mbr.isImplicitlyInherited)
+ if (mbr.isShadowedOrAmbiguousImplicit)
"implicit shadowed"
else
"implicit"
@@ -779,20 +770,21 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp
}
}{ if (isReduced) NodeSeq.Empty else {
mbr match {
- case tpl: DocTemplateEntity if !tpl.parentTypes.isEmpty =>
- <span class="result"> extends { typeToHtml(tpl.parentTypes.map(_._2), hasLinks) }</span>
-
case tme: MemberEntity if (tme.isDef || tme.isVal || tme.isLazyVal || tme.isVar) =>
<span class="result">: { typeToHtml(tme.resultType, hasLinks) }</span>
- case abt: AbstractType =>
+ case abt: MemberEntity with AbstractType =>
val b2s = boundsToHtml(abt.hi, abt.lo, hasLinks)
if (b2s != NodeSeq.Empty)
<span class="result">{ b2s }</span>
else NodeSeq.Empty
- case alt: AliasType =>
+ case alt: MemberEntity with AliasType =>
<span class="result"> = { typeToHtml(alt.alias, hasLinks) }</span>
+
+ case tpl: MemberTemplateEntity if !tpl.parentTypes.isEmpty =>
+ <span class="result"> extends { typeToHtml(tpl.parentTypes.map(_._2), hasLinks) }</span>
+
case _ => NodeSeq.Empty
}
}}
@@ -800,7 +792,7 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp
</xml:group>
mbr match {
case dte: DocTemplateEntity if !isSelf =>
- <h4 class="signature">{ inside(hasLinks = false, nameLink = relativeLinkTo(dte)) }</h4>
+ <h4 class="signature">{ inside(hasLinks = true, nameLink = relativeLinkTo(dte)) }</h4>
case _ if isSelf =>
<h4 id="signature" class="signature">{ inside(hasLinks = true) }</h4>
case _ =>
@@ -850,18 +842,13 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp
val link = relativeLinkTo(mbr)
myXml ++= <span class="name"><a href={link}>{str.substring(from, to)}</a></span>
case mbr: MemberEntity =>
- val anchor = "#" + mbr.name + defParamsString(mbr) + ":" + mbr.resultType.name
+ val anchor = "#" + mbr.signature
val link = relativeLinkTo(mbr.inTemplate)
myXml ++= <span class="name"><a href={link ++ anchor}>{str.substring(from, to)}</a></span>
}
index = to
}
}
- // function used in the MemberEntity case above
- def defParamsString(mbr: Entity):String = mbr match {
- case d:MemberEntity with Def => defParamsToString(d)
- case _ => ""
- }
if (index <= length-1)
myXml ++= codeStringToXml(str.substring(index, length ))
@@ -952,16 +939,18 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp
}
def makeDiagramHtml(tpl: DocTemplateEntity, diagram: Option[Diagram], description: String, id: String) = {
- 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>
+ 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
}
}
@@ -970,12 +959,5 @@ object Template {
/* Vlad: Lesson learned the hard way: don't put any stateful code that references the model here,
* it won't be garbage collected and you'll end up filling the heap with garbage */
- def isImplicit(mbr: MemberEntity) = mbr.byConversion.isDefined
- def isShadowedImplicit(mbr: MemberEntity): Boolean =
- mbr.byConversion.map(_.source.implicitsShadowing.get(mbr).map(_.isShadowed).getOrElse(false)).getOrElse(false)
- def isAmbiguousImplicit(mbr: MemberEntity): Boolean =
- mbr.byConversion.map(_.source.implicitsShadowing.get(mbr).map(_.isAmbiguous).getOrElse(false)).getOrElse(false)
- def isShadowedOrAmbiguousImplicit(mbr: MemberEntity) = isShadowedImplicit(mbr) || isAmbiguousImplicit(mbr)
-
def lowerFirstLetter(s: String) = if (s.length >= 1) s.substring(0,1).toLowerCase() + s.substring(1) else s
}
diff --git a/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotDiagramGenerator.scala b/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotDiagramGenerator.scala
index dc6f941c30..bae61f1a3b 100644
--- a/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotDiagramGenerator.scala
+++ b/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotDiagramGenerator.scala
@@ -25,7 +25,7 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator {
// maps an index to its corresponding node
private var index2Node: Map[Int, Node] = null
// true if the current diagram is a class diagram
- private var isClassDiagram = false
+ private var isInheritanceDiagram = false
// incoming implicit nodes (needed for determining the CSS class of a node)
private var incomingImplicitNodes: List[Node] = List()
// the suffix used when there are two many classes to show
@@ -66,15 +66,15 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator {
var superClasses = List[Node]()
var incomingImplicits = List[Node]()
var outgoingImplicits = List[Node]()
- isClassDiagram = false
+ isInheritanceDiagram = false
d match {
- case ClassDiagram(_thisNode, _superClasses, _subClasses, _incomingImplicits, _outgoingImplicits) =>
+ case InheritanceDiagram(_thisNode, _superClasses, _subClasses, _incomingImplicits, _outgoingImplicits) =>
def textTypeEntity(text: String) =
new TypeEntity {
val name = text
- def refEntity: SortedMap[Int, (TemplateEntity, Int)] = SortedMap()
+ def refEntity: SortedMap[Int, (LinkTo, Int)] = SortedMap()
}
// it seems dot chokes on node names over 8000 chars, so let's limit the size of the string
@@ -86,29 +86,29 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator {
// them by on node with a corresponding tooltip
superClasses = if (_superClasses.length > settings.docDiagramsMaxNormalClasses.value) {
val superClassesTooltip = Some(limitSize(_superClasses.map(_.tpe.name).mkString(", ")))
- List(NormalNode(textTypeEntity(_superClasses.length + MultiSuffix), None, superClassesTooltip))
+ List(NormalNode(textTypeEntity(_superClasses.length + MultiSuffix), None)(superClassesTooltip))
} else _superClasses
subClasses = if (_subClasses.length > settings.docDiagramsMaxNormalClasses.value) {
val subClassesTooltip = Some(limitSize(_subClasses.map(_.tpe.name).mkString(", ")))
- List(NormalNode(textTypeEntity(_subClasses.length + MultiSuffix), None, subClassesTooltip))
+ List(NormalNode(textTypeEntity(_subClasses.length + MultiSuffix), None)(subClassesTooltip))
} else _subClasses
incomingImplicits = if (_incomingImplicits.length > settings.docDiagramsMaxImplicitClasses.value) {
val incomingImplicitsTooltip = Some(limitSize(_incomingImplicits.map(_.tpe.name).mkString(", ")))
- List(ImplicitNode(textTypeEntity(_incomingImplicits.length + MultiSuffix), None, incomingImplicitsTooltip))
+ List(ImplicitNode(textTypeEntity(_incomingImplicits.length + MultiSuffix), None)(incomingImplicitsTooltip))
} else _incomingImplicits
outgoingImplicits = if (_outgoingImplicits.length > settings.docDiagramsMaxImplicitClasses.value) {
val outgoingImplicitsTooltip = Some(limitSize(_outgoingImplicits.map(_.tpe.name).mkString(", ")))
- List(ImplicitNode(textTypeEntity(_outgoingImplicits.length + MultiSuffix), None, outgoingImplicitsTooltip))
+ List(ImplicitNode(textTypeEntity(_outgoingImplicits.length + MultiSuffix), None)(outgoingImplicitsTooltip))
} else _outgoingImplicits
thisNode = _thisNode
nodes = List()
edges = (thisNode -> superClasses) :: subClasses.map(_ -> List(thisNode))
node2Index = (thisNode::subClasses:::superClasses:::incomingImplicits:::outgoingImplicits).zipWithIndex.toMap
- isClassDiagram = true
+ isInheritanceDiagram = true
incomingImplicitNodes = incomingImplicits
case _ =>
nodes = d.nodes
@@ -119,7 +119,7 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator {
index2Node = node2Index map {_.swap}
val implicitsDot = {
- if (!isClassDiagram) ""
+ if (!isInheritanceDiagram) ""
else {
// dot cluster containing thisNode
val thisCluster = "subgraph clusterThis {\n" +
@@ -242,6 +242,8 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator {
attr ++= classStyle
else if(node.isObjectNode)
attr ++= objectStyle
+ else if(node.isTypeNode)
+ attr ++= typeStyle
else
attr ++= defaultStyle
@@ -254,6 +256,8 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator {
img = "class_diagram.png"
else if(node.isObjectNode)
img = "object_diagram.png"
+ else if(node.isTypeNode)
+ img = "type_diagram.png"
if(!img.equals("")) {
img = "<TD><IMG SCALE=\"TRUE\" SRC=\"" + settings.outdir.value + "/lib/" + img + "\" /></TD>"
@@ -307,6 +311,8 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator {
space + "trait"
else if (node.isObjectNode)
space + "object"
+ else if (node.isTypeNode)
+ space + "type"
else
default
@@ -360,7 +366,7 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator {
private def transform(e:scala.xml.Node): scala.xml.Node = e match {
// add an id and class attribute to the SVG element
case Elem(prefix, "svg", attribs, scope, child @ _*) => {
- val klass = if (isClassDiagram) "class-diagram" else "package-diagram"
+ val klass = if (isInheritanceDiagram) "class-diagram" else "package-diagram"
Elem(prefix, "svg", attribs, scope, child map(x => transform(x)) : _*) %
new UnprefixedAttribute("id", "graph" + counter, Null) %
new UnprefixedAttribute("class", klass, Null)
@@ -491,6 +497,12 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator {
"fontcolor" -> "#ffffff"
)
+ private val typeStyle = Map(
+ "color" -> "#115F3B",
+ "fillcolor" -> "#0A955B",
+ "fontcolor" -> "#ffffff"
+ )
+
private def flatten(attributes: Map[String, String]) = attributes.map{ case (key, value) => key + "=\"" + value + "\"" }.mkString(", ")
private val graphAttributesStr = graphAttributes.map{ case (key, value) => key + "=\"" + value + "\";\n" }.mkString
diff --git a/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotRunner.scala b/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotRunner.scala
index 37600fa908..3040278290 100644
--- a/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotRunner.scala
+++ b/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotRunner.scala
@@ -28,7 +28,7 @@ class DotRunner(settings: doc.Settings) {
if (dotProcess == null) {
if (dotRestarts < settings.docDiagramsDotRestart.value) {
if (dotRestarts != 0)
- settings.printMsg("A new graphviz dot process will be created...\n")
+ settings.printMsg("Graphviz will be restarted...\n")
dotRestarts += 1
dotProcess = new DotProcess(settings)
} else
@@ -46,7 +46,7 @@ class DotRunner(settings: doc.Settings) {
if (dotRestarts == settings.docDiagramsDotRestart.value) {
settings.printMsg("\n")
settings.printMsg("**********************************************************************")
- settings.printMsg("Diagrams will be disabled for this run beucause the graphviz dot tool")
+ settings.printMsg("Diagrams will be disabled for this run because the graphviz dot tool")
settings.printMsg("has malfunctioned too many times. These scaladoc flags may help:")
settings.printMsg("")
val baseList = List(settings.docDiagramsDebug,
@@ -145,9 +145,10 @@ class DotProcess(settings: doc.Settings) {
settings.printMsg("**********************************************************************")
} else {
// we shouldn't just sit there for 50s not reporting anything, no?
- settings.printMsg("Graphviz dot encountered an error when generating the diagram for")
- settings.printMsg(templateName + ". Use the " + settings.docDiagramsDebug.name + " flag")
- settings.printMsg("for more information.")
+ settings.printMsg("Graphviz dot encountered an error when generating the diagram for:")
+ settings.printMsg(templateName)
+ settings.printMsg("These are usually spurious errors, but if you notice a persistant error on")
+ settings.printMsg("a diagram, please use the " + settings.docDiagramsDebug.name + " flag and report a bug with the output.")
}
}
}
diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/diagrams.css b/src/compiler/scala/tools/nsc/doc/html/resource/lib/diagrams.css
index 04d29580b7..5fe33f72f5 100644
--- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/diagrams.css
+++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/diagrams.css
@@ -119,6 +119,14 @@ svg.class-diagram .node.this.trait.over polygon
fill: #235d7b;
}
+svg.package-diagram .node.type.over polygon,
+svg.class-diagram .node.this.type.over polygon
+{
+ fill: #098552;
+ fill: #04663e;
+}
+
+
svg.package-diagram .node.object.over polygon
{
fill: #183377;
diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js b/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js
index eb7f752440..f29438edfb 100644
--- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js
+++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js
@@ -15,15 +15,15 @@ var lastHash = "";
$(document).ready(function() {
$('body').layout({ west__size: '20%' });
- $('#browser').layout({
+ $('#browser').layout({
center__paneSelector: ".ui-west-center"
//,center__initClosed:true
,north__paneSelector: ".ui-west-north"
- });
+ });
$('iframe').bind("load", function(){
var subtitle = $(this).contents().find('title').text();
$(document).attr('title', (title ? title + " - " : "") + subtitle);
-
+
setUrlFragmentFromFrameSrc();
});
@@ -81,16 +81,16 @@ function setUrlFragmentFromFrameSrc() {
var commonLength = location.pathname.lastIndexOf("/");
var frameLocation = frames["template"].location;
var relativePath = frameLocation.pathname.slice(commonLength + 1);
-
+
if(!relativePath || frameLocation.pathname.indexOf("/") < 0)
return;
-
+
// Add #, remove ".html" and replace "/" with "."
fragment = "#" + relativePath.replace(/\.html$/, "").replace(/\//g, ".");
-
+
// Add the frame's hash after an @
if(frameLocation.hash) fragment += ("@" + frameLocation.hash.slice(1));
-
+
// Use replace to not add history items
lastFragment = fragment;
location.replace(fragment);
@@ -109,7 +109,7 @@ var Index = {};
if (type == 'object') {
href = t['object'];
} else {
- href = t['class'] || t['trait'] || t['case class'];
+ href = t['class'] || t['trait'] || t['case class'] || t['type'];
}
return [
'<a class="tplshow" target="template" href="',
@@ -142,10 +142,10 @@ var Index = {};
inner += openLink(template, 'object');
}
- if (template['class'] || template['trait'] || template['case class']) {
+ if (template['class'] || template['trait'] || template['case class'] || template['type']) {
inner += (inner == '') ?
'<div class="placeholder" />' : '</a>';
- inner += openLink(template, template['trait'] ? 'trait' : 'class');
+ inner += openLink(template, template['trait'] ? 'trait' : template['type'] ? 'type' : 'class');
} else {
inner += '<div class="placeholder"/>';
}
@@ -245,6 +245,7 @@ function configureEntityList() {
function prepareEntityList() {
var classIcon = $("#library > img.class");
var traitIcon = $("#library > img.trait");
+ var typeIcon = $("#library > img.type");
var objectIcon = $("#library > img.object");
var packageIcon = $("#library > img.package");
@@ -252,6 +253,7 @@ function prepareEntityList() {
$('#tpl li.pack').each(function () {
$("span.class", this).each(function() { $(this).replaceWith(classIcon.clone()); });
$("span.trait", this).each(function() { $(this).replaceWith(traitIcon.clone()); });
+ $("span.type", this).each(function() { $(this).replaceWith(typeIcon.clone()); });
$("span.object", this).each(function() { $(this).replaceWith(objectIcon.clone()); });
$("span.package", this).each(function() { $(this).replaceWith(packageIcon.clone()); });
});
@@ -265,11 +267,11 @@ function keyboardScrolldownLeftPane() {
scheduler.add("init", function() {
$("#textfilter input").blur();
var $items = $("#tpl li");
- $items.first().addClass('selected');
+ $items.first().addClass('selected');
$(window).bind("keydown", function(e) {
var $old = $items.filter('.selected'),
- $new;
+ $new;
switch ( e.keyCode ) {
@@ -286,7 +288,7 @@ function keyboardScrolldownLeftPane() {
case 27: // escape
$old.removeClass('selected');
$(window).unbind(e);
- $("#textfilter input").focus();
+ $("#textfilter input").focus();
break;
@@ -296,7 +298,7 @@ function keyboardScrolldownLeftPane() {
if (!$new.length) {
$new = $old.parent().prev();
}
-
+
if ($new.is('ol') && $new.children(':last').is('ol')) {
$new = $new.children().children(':last');
} else if ($new.is('ol')) {
@@ -313,17 +315,17 @@ function keyboardScrolldownLeftPane() {
if ($new.is('ol')) {
$new = $new.children(':first');
}
- break;
- }
-
+ break;
+ }
+
if ($new.is('li')) {
$old.removeClass('selected');
- $new.addClass('selected');
+ $new.addClass('selected');
} else if (e.keyCode == 38) {
$(window).unbind(e);
$("#textfilter input").focus();
}
- });
+ });
});
}
@@ -342,13 +344,13 @@ function configureTextFilter() {
$("#template").contents().find("#mbrsel-input").focus();
input.attr("value", "");
return false;
- }
+ }
if (event.keyCode == 40) { // down arrow
$(window).unbind("keydown");
keyboardScrolldownLeftPane();
return false;
- }
- textFilter();
+ }
+ textFilter();
});
input.focus(function(event) { input.select(); });
});
@@ -419,7 +421,7 @@ function textFilter() {
});
configureHideFilter();
};
-
+
scheduler.add('filter', searchLoop);
}
diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/object_to_type_big.png b/src/compiler/scala/tools/nsc/doc/html/resource/lib/object_to_type_big.png
new file mode 100644
index 0000000000..7502942eb6
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/object_to_type_big.png
Binary files differ
diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css
index 1bee55313b..6fb7953724 100644
--- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css
+++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css
@@ -253,6 +253,17 @@ dl.attributes > dd {
color: white;
}
+#groupedMembers > div.group > h3 {
+ background: #dadada url("typebg.gif") repeat-x bottom left; /* green */
+ height: 17px;
+ font-size: 12pt;
+}
+
+#groupedMembers > div.group > h3 * {
+ color: white;
+}
+
+
/* Member cells */
div.members > ol {
@@ -516,6 +527,20 @@ div.members > ol > li:last-child {
/* Comments structured layout */
+.group > div.comment {
+ padding-top: 5px;
+ padding-bottom: 5px;
+ padding-right: 5px;
+ padding-left: 5px;
+ border: 1px solid #ddd;
+ background-color: #eeeee;
+ margin-top:5px;
+ margin-bottom:5px;
+ margin-right:5px;
+ margin-left:5px;
+ display: block;
+}
+
p.comment {
display: block;
margin-left: 14.7em;
diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js
index c418c3280b..afd0293fe1 100644
--- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js
+++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js
@@ -43,20 +43,20 @@ $(document).ready(function(){
case 33: //page up
input.val("");
- filter(false);
+ filter(false);
break;
case 34: //page down
input.val("");
- filter(false);
- break;
+ filter(false);
+ break;
- default:
+ default:
window.scrollTo(0, $("#mbrsel").offset().top);
- filter(true);
+ filter(true);
break;
- }
+ }
});
input.focus(function(event) {
input.select();
@@ -70,7 +70,7 @@ $(document).ready(function(){
if (event.keyCode == 9) { // tab
$("#index-input", window.parent.document).focus();
input.attr("value", "");
- return false;
+ return false;
}
});
@@ -135,18 +135,21 @@ $(document).ready(function(){
});
$("#order > ol > li.alpha").click(function() {
if ($(this).hasClass("out")) {
- $(this).removeClass("out").addClass("in");
- $("#order > ol > li.inherit").removeClass("in").addClass("out");
orderAlpha();
};
})
$("#order > ol > li.inherit").click(function() {
if ($(this).hasClass("out")) {
- $(this).removeClass("out").addClass("in");
- $("#order > ol > li.alpha").removeClass("in").addClass("out");
orderInherit();
};
});
+ $("#order > ol > li.group").click(function() {
+ if ($(this).hasClass("out")) {
+ orderGroup();
+ };
+ });
+ $("#groupedMembers").hide();
+
initInherit();
// Create tooltips
@@ -202,9 +205,14 @@ $(document).ready(function(){
// Set parent window title
windowTitle();
+
+ if ($("#order > ol > li.group").length == 1) { orderGroup(); };
});
function orderAlpha() {
+ $("#order > ol > li.alpha").removeClass("out").addClass("in");
+ $("#order > ol > li.inherit").removeClass("in").addClass("out");
+ $("#order > ol > li.group").removeClass("in").addClass("out");
$("#template > div.parent").hide();
$("#template > div.conversion").hide();
$("#mbrsel > div[id=ancestors]").show();
@@ -212,12 +220,25 @@ function orderAlpha() {
};
function orderInherit() {
+ $("#order > ol > li.inherit").removeClass("out").addClass("in");
+ $("#order > ol > li.alpha").removeClass("in").addClass("out");
+ $("#order > ol > li.group").removeClass("in").addClass("out");
$("#template > div.parent").show();
$("#template > div.conversion").show();
$("#mbrsel > div[id=ancestors]").hide();
filter();
};
+function orderGroup() {
+ $("#order > ol > li.group").removeClass("out").addClass("in");
+ $("#order > ol > li.alpha").removeClass("in").addClass("out");
+ $("#order > ol > li.inherit").removeClass("in").addClass("out");
+ $("#template > div.parent").hide();
+ $("#template > div.conversion").hide();
+ $("#mbrsel > div[id=ancestors]").show();
+ filter();
+};
+
/** Prepares the DOM for inheritance-based display. To do so it will:
* - hide all statically-generated parents headings;
* - copy all members from the value and type members lists (flat members) to corresponding lists nested below the
@@ -225,44 +246,74 @@ function orderInherit() {
* - initialises a control variable used by the filter method to control whether filtering happens on flat members
* or on inheritance-grouped members. */
function initInherit() {
- // parents is a map from fully-qualified names to the DOM node of parent headings.
- var parents = new Object();
+ // inheritParents is a map from fully-qualified names to the DOM node of parent headings.
+ var inheritParents = new Object();
+ var groupParents = new Object();
$("#inheritedMembers > div.parent").each(function(){
- parents[$(this).attr("name")] = $(this);
+ inheritParents[$(this).attr("name")] = $(this);
});
$("#inheritedMembers > div.conversion").each(function(){
- parents[$(this).attr("name")] = $(this);
+ inheritParents[$(this).attr("name")] = $(this);
+ });
+ $("#groupedMembers > div.group").each(function(){
+ groupParents[$(this).attr("name")] = $(this);
});
+
$("#types > ol > li").each(function(){
var mbr = $(this);
this.mbrText = mbr.find("> .fullcomment .cmt").text();
var qualName = mbr.attr("name");
var owner = qualName.slice(0, qualName.indexOf("#"));
var name = qualName.slice(qualName.indexOf("#") + 1);
- var parent = parents[owner];
- if (parent != undefined) {
- var types = $("> .types > ol", parent);
+ var inheritParent = inheritParents[owner];
+ if (inheritParent != undefined) {
+ var types = $("> .types > ol", inheritParent);
+ if (types.length == 0) {
+ inheritParent.append("<div class='types members'><h3>Type Members</h3><ol></ol></div>");
+ types = $("> .types > ol", inheritParent);
+ }
+ var clone = mbr.clone();
+ clone[0].mbrText = this.mbrText;
+ types.append(clone);
+ }
+ var group = mbr.attr("group")
+ var groupParent = groupParents[group];
+ if (groupParent != undefined) {
+ var types = $("> .types > ol", groupParent);
if (types.length == 0) {
- parent.append("<div class='types members'><h3>Type Members</h3><ol></ol></div>");
- types = $("> .types > ol", parent);
+ groupParent.append("<div class='types members'><ol></ol></div>");
+ types = $("> .types > ol", groupParent);
}
var clone = mbr.clone();
clone[0].mbrText = this.mbrText;
types.append(clone);
}
});
+
$("#values > ol > li").each(function(){
var mbr = $(this);
this.mbrText = mbr.find("> .fullcomment .cmt").text();
var qualName = mbr.attr("name");
var owner = qualName.slice(0, qualName.indexOf("#"));
var name = qualName.slice(qualName.indexOf("#") + 1);
- var parent = parents[owner];
- if (parent != undefined) {
- var values = $("> .values > ol", parent);
+ var inheritParent = inheritParents[owner];
+ if (inheritParent != undefined) {
+ var values = $("> .values > ol", inheritParent);
if (values.length == 0) {
- parent.append("<div class='values members'><h3>Value Members</h3><ol></ol></div>");
- values = $("> .values > ol", parent);
+ inheritParent.append("<div class='values members'><h3>Value Members</h3><ol></ol></div>");
+ values = $("> .values > ol", inheritParent);
+ }
+ var clone = mbr.clone();
+ clone[0].mbrText = this.mbrText;
+ values.append(clone);
+ }
+ var group = mbr.attr("group")
+ var groupParent = groupParents[group];
+ if (groupParent != undefined) {
+ var values = $("> .values > ol", groupParent);
+ if (values.length == 0) {
+ groupParent.append("<div class='values members'><ol></ol></div>");
+ values = $("> .values > ol", groupParent);
}
var clone = mbr.clone();
clone[0].mbrText = this.mbrText;
@@ -275,6 +326,9 @@ function initInherit() {
$("#inheritedMembers > div.conversion").each(function() {
if ($("> div.members", this).length == 0) { $(this).remove(); };
});
+ $("#groupedMembers > div.group").each(function() {
+ if ($("> div.members", this).length == 0) { $(this).remove(); };
+ });
};
/* filter used to take boolean scrollToMember */
@@ -284,26 +338,43 @@ function filter() {
var queryRegExp = new RegExp(query, "i");
var privateMembersHidden = $("#visbl > ol > li.public").hasClass("in");
var orderingAlphabetic = $("#order > ol > li.alpha").hasClass("in");
- var hiddenSuperclassElementsLinearization = orderingAlphabetic ? $("#linearization > li.out") : $("#linearization > li:gt(0)");
+ var orderingInheritance = $("#order > ol > li.inherit").hasClass("in");
+ var orderingGroups = $("#order > ol > li.group").hasClass("in");
+ var hiddenSuperclassElementsLinearization = orderingInheritance ? $("#linearization > li:gt(0)") : $("#linearization > li.out");
var hiddenSuperclassesLinearization = hiddenSuperclassElementsLinearization.map(function() {
return $(this).attr("name");
}).get();
- var hiddenSuperclassElementsImplicits = orderingAlphabetic ? $("#implicits > li.out") : $("#implicits > li");
+ var hiddenSuperclassElementsImplicits = orderingInheritance ? $("#implicits > li") : $("#implicits > li.out");
var hiddenSuperclassesImplicits = hiddenSuperclassElementsImplicits.map(function() {
return $(this).attr("name");
}).get();
var hideInheritedMembers;
- if(orderingAlphabetic) {
+ if (orderingAlphabetic) {
+ $("#allMembers").show();
$("#inheritedMembers").hide();
+ $("#groupedMembers").hide();
hideInheritedMembers = true;
$("#allMembers > .members").each(filterFunc);
- }
- else {
- $("#inheritedMembers").show();
+ } else if (orderingGroups) {
+ $("#groupedMembers").show();
+ $("#inheritedMembers").hide();
+ $("#allMembers").hide();
hideInheritedMembers = true;
- $("#allMembers > .members").each(filterFunc);
+ $("#groupedMembers > .group > .members").each(filterFunc);
+ $("#groupedMembers > div.group").each(function() {
+ $(this).show();
+ if ($("> div.members", this).not(":hidden").length == 0) {
+ $(this).hide();
+ } else {
+ $(this).show();
+ }
+ });
+ } else if (orderingInheritance) {
+ $("#inheritedMembers").show();
+ $("#groupedMembers").hide();
+ $("#allMembers").hide();
hideInheritedMembers = false;
$("#inheritedMembers > .parent > .members").each(filterFunc);
$("#inheritedMembers > .conversion > .members").each(filterFunc);
diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/type.png b/src/compiler/scala/tools/nsc/doc/html/resource/lib/type.png
new file mode 100644
index 0000000000..366ec4e992
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/type.png
Binary files differ
diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/type_big.png b/src/compiler/scala/tools/nsc/doc/html/resource/lib/type_big.png
new file mode 100644
index 0000000000..df0dc118bf
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/type_big.png
Binary files differ
diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/type_diagram.png b/src/compiler/scala/tools/nsc/doc/html/resource/lib/type_diagram.png
new file mode 100644
index 0000000000..d6fbb84ff2
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/type_diagram.png
Binary files differ
diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/type_to_object_big.png b/src/compiler/scala/tools/nsc/doc/html/resource/lib/type_to_object_big.png
new file mode 100644
index 0000000000..1bd2833a63
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/type_to_object_big.png
Binary files differ
diff --git a/src/compiler/scala/tools/nsc/doc/model/Entity.scala b/src/compiler/scala/tools/nsc/doc/model/Entity.scala
index 2901daafd6..16ade0787e 100644
--- a/src/compiler/scala/tools/nsc/doc/model/Entity.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/Entity.scala
@@ -53,6 +53,15 @@ trait Entity {
/** The kind of the entity */
def kind: String
+
+ /** Whether or not the template was defined in a package object */
+ def inPackageObject: Boolean
+
+ /** Indicates whether this entity lives in the types namespace (classes, traits, abstract/alias types) */
+ def isType: Boolean
+
+ /** Indicates whether this entity lives in the terms namespace (objects, packages, methods, values) */
+ def isTerm: Boolean
}
object Entity {
@@ -88,17 +97,14 @@ trait TemplateEntity extends Entity {
/** Whether documentation is available for this template. */
def isDocTemplate: Boolean
+ /** Whether documentation is available for this template. */
+ def isNoDocMemberTemplate: Boolean
+
/** Whether this template is a case class. */
def isCaseClass: Boolean
- /** Whether or not the template was defined in a package object */
- def inPackageObject: Boolean
-
/** The self-type of this template, if it differs from the template type. */
def selfType : Option[TypeEntity]
-
- /** The type of this entity, with type members */
- def ownType: TypeEntity
}
@@ -110,6 +116,9 @@ trait MemberEntity extends Entity {
/** The comment attached to this member, if any. */
def comment: Option[Comment]
+ /** The group this member is from */
+ def group: String
+
/** The template of which this entity is a member. */
def inTemplate: DocTemplateEntity
@@ -179,11 +188,28 @@ trait MemberEntity extends Entity {
/** If this symbol is a use case, the useCaseOf will contain the member it was derived from, containing the full
* signature and the complete parameter descriptions. */
- def useCaseOf: Option[MemberEntity] = None
+ def useCaseOf: Option[MemberEntity]
/** If this member originates from an implicit conversion, we set the implicit information to the correct origin */
def byConversion: Option[ImplicitConversion]
+
+ /** The identity of this member, used for linking */
+ def signature: String
+
+ /** Indicates whether the member is inherited by implicit conversion */
+ def isImplicitlyInherited: Boolean
+
+ /** Indicates whether there is another member with the same name in the template that will take precendence */
+ def isShadowedImplicit: Boolean
+
+ /** Indicates whether there are other implicitly inherited members that have similar signatures (and thus they all
+ * become ambiguous) */
+ def isAmbiguousImplicit: Boolean
+
+ /** Indicates whether the implicitly inherited member is shadowed or ambiguous in its template */
+ def isShadowedOrAmbiguousImplicit: Boolean
}
+
object MemberEntity {
// Oh contravariance, contravariance, wherefore art thou contravariance?
// Note: the above works for both the commonly misunderstood meaning of the line and the real one.
@@ -195,24 +221,38 @@ trait HigherKinded {
/** The type parameters of this entity. */
def typeParams: List[TypeParam]
-
}
/** A template (class, trait, object or package) which is referenced in the universe, but for which no further
* documentation is available. Only templates for which a source file is given are documented by Scaladoc. */
trait NoDocTemplate extends TemplateEntity {
- def kind = "<no doc>"
+ def kind =
+ if (isClass) "class"
+ else if (isTrait) "trait"
+ else if (isObject) "object"
+ else ""
}
-/** TODO: Document */
-trait NoDocTemplateMemberEntity extends TemplateEntity with MemberEntity {
- def kind = "<no doc, mbr>"
+/** An inherited template that was not documented in its original owner - example:
+ * in classpath: trait T { class C } -- T (and implicitly C) are not documented
+ * in the source: trait U extends T -- C appears in U as a MemberTemplateImpl
+ * -- that is, U has a member for it but C doesn't get its own page */
+trait MemberTemplateEntity extends TemplateEntity with MemberEntity with HigherKinded {
+
+ /** The value parameters of this case class, or an empty list if this class is not a case class. As case class value
+ * parameters cannot be curried, the outer list has exactly one element. */
+ def valueParams: List[List[ValueParam]]
+
+ /** The direct super-type of this template
+ e.g: {{{class A extends B[C[Int]] with D[E]}}} will have two direct parents: class B and D
+ NOTE: we are dropping the refinement here! */
+ def parentTypes: List[(TemplateEntity, TypeEntity)]
}
/** A template (class, trait, object or package) for which documentation is available. Only templates for which
* a source file is given are documented by Scaladoc. */
-trait DocTemplateEntity extends TemplateEntity with MemberEntity {
+trait DocTemplateEntity extends MemberTemplateEntity {
/** The list of templates such that each is a member of the template that follows it; the first template is always
* this template, the last the root package entity. */
@@ -226,11 +266,6 @@ trait DocTemplateEntity extends TemplateEntity with MemberEntity {
* only if the `docsourceurl` setting has been set. */
def sourceUrl: Option[java.net.URL]
- /** The direct super-type of this template
- e.g: {{{class A extends B[C[Int]] with D[E]}}} will have two direct parents: class B and D
- NOTE: we are dropping the refinement here! */
- def parentTypes: List[(TemplateEntity, TypeEntity)]
-
/** All class, trait and object templates which are part of this template's linearization, in lineratization order.
* This template's linearization contains all of its direct and indirect super-classes and super-traits. */
def linearizationTemplates: List[TemplateEntity]
@@ -253,7 +288,7 @@ trait DocTemplateEntity extends TemplateEntity with MemberEntity {
/** All templates that are members of this template. If this template is a package, only templates for which
* documentation is available in the universe (`DocTemplateEntity`) are listed. */
- def templates: List[DocTemplateEntity]
+ def templates: List[TemplateEntity with MemberEntity]
/** All methods that are members of this template. */
def methods: List[Def]
@@ -267,6 +302,12 @@ trait DocTemplateEntity extends TemplateEntity with MemberEntity {
/** All type aliases that are members of this template. */
def aliasTypes: List[AliasType]
+ /** The primary constructor of this class, if it has been defined. */
+ def primaryConstructor: Option[Constructor]
+
+ /** All constructors of this class, including the primary constructor. */
+ def constructors: List[Constructor]
+
/** The companion of this template, or none. If a class and an object are defined as a pair of the same name, the
* other entity of the pair is the companion. */
def companion: Option[DocTemplateEntity]
@@ -289,41 +330,35 @@ trait DocTemplateEntity extends TemplateEntity with MemberEntity {
/** If this template contains other templates, such as classes and traits, they will be shown in this diagram */
def contentDiagram: Option[Diagram]
-}
+ /** Returns the group description taken either from this template or its linearizationTypes */
+ def groupDescription(group: String): Option[Body]
+
+ /** Returns the group description taken either from this template or its linearizationTypes */
+ def groupPriority(group: String): Int
+
+ /** Returns the group description taken either from this template or its linearizationTypes */
+ def groupName(group: String): String
+}
/** A trait template. */
-trait Trait extends DocTemplateEntity with HigherKinded {
+trait Trait extends MemberTemplateEntity {
def kind = "trait"
}
-
/** A class template. */
-trait Class extends Trait with HigherKinded {
-
- /** The primary constructor of this class, if it has been defined. */
- def primaryConstructor: Option[Constructor]
-
- /** All constructors of this class, including the primary constructor. */
- def constructors: List[Constructor]
-
- /** The value parameters of this case class, or an empty list if this class is not a case class. As case class value
- * parameters cannot be curried, the outer list has exactly one element. */
- def valueParams: List[List[ValueParam]]
-
+trait Class extends MemberTemplateEntity {
override def kind = "class"
}
-
/** An object template. */
-trait Object extends DocTemplateEntity {
+trait Object extends MemberTemplateEntity {
def kind = "object"
}
-
/** A package template. A package is in the universe if it is declared as a package object, or if it
* contains at least one template. */
-trait Package extends Object {
+trait Package extends DocTemplateEntity {
/** The package of which this package is a member. */
def inTemplate: Package
@@ -354,7 +389,6 @@ trait NonTemplateMemberEntity extends MemberEntity {
/** Whether this member is a bridge member. A bridge member does only exist for binary compatibility reasons
* and should not appear in ScalaDoc. */
def isBridge: Boolean
-
}
@@ -391,7 +425,7 @@ trait Val extends NonTemplateMemberEntity {
/** An abstract type member of a template. */
-trait AbstractType extends NonTemplateMemberEntity with HigherKinded {
+trait AbstractType extends MemberTemplateEntity with HigherKinded {
/** The lower bound for this abstract type, if it has been defined. */
def lo: Option[TypeEntity]
@@ -404,7 +438,7 @@ trait AbstractType extends NonTemplateMemberEntity with HigherKinded {
/** An type alias of a template. */
-trait AliasType extends NonTemplateMemberEntity with HigherKinded {
+trait AliasType extends MemberTemplateEntity with HigherKinded {
/** The type aliased by this type alias. */
def alias: TypeEntity
@@ -495,6 +529,9 @@ trait ImplicitConversion {
/** The members inherited by this implicit conversion */
def members: List[MemberEntity]
+
+ /** Is this a common implicit conversion (aka conversion that affects all classes, in Predef?) */
+ def isCommonConversion: Boolean
}
/** Shadowing captures the information that the member is shadowed by some other members
diff --git a/src/compiler/scala/tools/nsc/doc/model/IndexModelFactory.scala b/src/compiler/scala/tools/nsc/doc/model/IndexModelFactory.scala
index 6392de22ff..f4c96505a7 100755
--- a/src/compiler/scala/tools/nsc/doc/model/IndexModelFactory.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/IndexModelFactory.scala
@@ -36,7 +36,6 @@ object IndexModelFactory {
} + d
this(firstLetter) = letter + (d.name -> members)
}
-
}
//@scala.annotation.tailrec // TODO
@@ -46,11 +45,7 @@ object IndexModelFactory {
case tpl: DocTemplateEntity =>
result.addMember(tpl)
gather(tpl)
- case alias: AliasType =>
- result.addMember(alias)
- case absType: AbstractType =>
- result.addMember(absType)
- case non: NonTemplateMemberEntity if !non.isConstructor =>
+ case non: MemberEntity if !non.isConstructor =>
result.addMember(non)
case x @ _ =>
}
diff --git a/src/compiler/scala/tools/nsc/doc/model/Links.scala b/src/compiler/scala/tools/nsc/doc/model/Links.scala
new file mode 100644
index 0000000000..b76dee0f14
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/doc/model/Links.scala
@@ -0,0 +1,24 @@
+/* NSC -- new Scala compiler
+ * Copyright 2007-2011 LAMP/EPFL
+ */
+
+package scala.tools.nsc
+package doc
+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
+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 {
+ // this makes it easier to create links
+ def apply(tpl: TemplateEntity) = tpl match {
+ case dtpl: DocTemplateEntity => new LinkToTpl(dtpl)
+ case ntpl: TemplateEntity => new Tooltip(ntpl.qualifiedName)
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/doc/model/MemberLookup.scala b/src/compiler/scala/tools/nsc/doc/model/MemberLookup.scala
new file mode 100644
index 0000000000..277e86f9af
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/doc/model/MemberLookup.scala
@@ -0,0 +1,188 @@
+package scala.tools.nsc
+package doc
+package model
+
+import comment._
+
+import scala.reflect.internal.util.FakePos //Position
+
+/** This trait extracts all required information for documentation from compilation units */
+trait MemberLookup {
+ thisFactory: ModelFactory =>
+
+ import global._
+
+ def makeEntityLink(title: Inline, pos: Position, query: String, inTplOpt: Option[DocTemplateImpl]) =
+ new EntityLink(title) { lazy val link = memberLookup(pos, query, inTplOpt) }
+
+ def memberLookup(pos: Position, query: String, inTplOpt: Option[DocTemplateImpl]): LinkTo = {
+ assert(modelFinished)
+
+ 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)
+
+ // (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
+ }
+ if (currentTpl == null) println("\n\n\n\n\nnull found in:" + inTplOpt + "\n\n\n\n\n\n\n\n")
+ }
+
+ // (3) Look at external links
+ if (linkTo.isEmpty) {
+ // TODO: IF THIS IS THE ROOT PACKAGE, LOOK AT EXTERNAL LINKS
+ }
+
+ // (4) if we still haven't found anything, create a tooltip, if we found too many, report
+ if (linkTo.isEmpty){
+ if (!settings.docNoLinkWarnings.value)
+ reporter.warning(pos, "Could not find any member to link for \"" + query + "\".")
+ Tooltip(query)
+ } else {
+ if (linkTo.length > 1) {
+
+ val chosen =
+ if (linkTo.exists(_.isInstanceOf[LinkToMember]))
+ linkTo.collect({case lm: LinkToMember => lm}).min(Ordering[MemberEntity].on[LinkToMember](_.mbr))
+ else
+ linkTo.head
+
+ def linkToString(link: LinkTo) = {
+ val description =
+ link match {
+ case lm@LinkToMember(mbr, inTpl) => " * " + mbr.kind + " \"" + mbr.signature + "\" in " + inTpl.kind + " " + inTpl.qualifiedName
+ case lt@LinkToTpl(tpl) => " * " + tpl.kind + " \"" + tpl.qualifiedName + "\""
+ case other => " * " + other.toString
+ }
+ val chosenInfo =
+ if (link == chosen)
+ " [chosen]"
+ else
+ ""
+ description + chosenInfo + "\n"
+ }
+ if (!settings.docNoLinkWarnings.value)
+ reporter.warning(pos,
+ "The link target \"" + query + "\" is ambiguous. Several (possibly overloaded) members fit the target:\n" +
+ linkTo.map(link => linkToString(link)).mkString +
+ (if (MemberLookup.showExplanation)
+ "\n\n" +
+ "Quick crash course on using Scaladoc links\n" +
+ "==========================================\n" +
+ "Disambiguating terms and types: Prefix terms with '$' and types with '!' in case both names are in use:\n" +
+ " - [[scala.collection.immutable.List!.apply class List's apply method]] and\n" +
+ " - [[scala.collection.immutable.List$.apply object List's apply method]]\n" +
+ "Disambiguating overloaded members: If a term is overloaded, you can indicate the first part of its signature followed by *:\n" +
+ " - [[[scala.collection.immutable.List$.fill[A](Int)(⇒A):List[A]* Fill with a single parameter]]]\n" +
+ " - [[[scala.collection.immutable.List$.fill[A](Int,Int)(⇒A):List[List[A]]* Fill with a two parameters]]]\n" +
+ "Notes: \n" +
+ " - you can use any number of matching square brackets to avoid interference with the signature\n" +
+ " - you can use \\. to escape dots in prefixes (don't forget to use * at the end to match the signature!)\n" +
+ " - you can use \\# to escape hashes, otherwise they will be considered as delimiters, like dots.\n"
+ else "")
+ )
+ chosen
+ } else
+ linkTo.head
+ }
+ }
+
+ private abstract class SearchStrategy
+ private object BothTypeAndTerm extends SearchStrategy
+ private object OnlyType extends SearchStrategy
+ private object OnlyTerm extends SearchStrategy
+
+ private def lookupInRootPackage(pos: Position, members: List[String]) = lookupInTemplate(pos, members, makeRootPackage)
+
+ private def lookupInTemplate(pos: Position, members: List[String], inTpl: DocTemplateImpl): List[LinkTo] = {
+ // 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:
+ // - at each step we do a DFS search with the prefered strategy
+ // - if the search doesn't return any members, we backtrack on the last decision
+ // * 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 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)
+ })
+
+ case tplName::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
+ }
+ //println("lookupInTemplate(" + members + ", " + inTpl + ") => " + result)
+ result
+ }
+
+ private def lookupInTemplate(pos: Position, member: String, inTpl: DocTemplateImpl, strategy: SearchStrategy): List[MemberImpl] = {
+ val name = member.stripSuffix("$").stripSuffix("!").stripSuffix("*")
+ val result = if (member.endsWith("$"))
+ inTpl.members.filter(mbr => (mbr.name == name) && (mbr.isTerm))
+ else if (member.endsWith("!"))
+ inTpl.members.filter(mbr => (mbr.name == name) && (mbr.isType))
+ else if (member.endsWith("*"))
+ inTpl.members.filter(mbr => (mbr.signature.startsWith(name)))
+ else {
+ if (strategy == BothTypeAndTerm)
+ inTpl.members.filter(_.name == name)
+ else if (strategy == OnlyType)
+ inTpl.members.filter(mbr => (mbr.name == name) && (mbr.isType))
+ else if (strategy == OnlyTerm)
+ inTpl.members.filter(mbr => (mbr.name == name) && (mbr.isTerm))
+ else
+ Nil
+ }
+
+ //println("lookupInTemplate(" + member + ", " + inTpl + ") => " + result)
+ result
+ }
+
+ private def breakMembers(query: String): List[String] = {
+ // Okay, how does this work? Well: you split on . but you don't want to split on \. => thus the ugly regex
+ // query.split((?<=[^\\\\])\\.).map(_.replaceAll("\\."))
+ // The same code, just faster:
+ var members = List[String]()
+ var index = 0
+ var last_index = 0
+ val length = query.length
+ while (index < length) {
+ if ((query.charAt(index) == '.' || query.charAt(index) == '#') &&
+ ((index == 0) || (query.charAt(index-1) != '\\'))) {
+
+ members ::= query.substring(last_index, index).replaceAll("\\\\([#\\.])", "$1")
+ last_index = index + 1
+ }
+ index += 1
+ }
+ if (last_index < length)
+ members ::= query.substring(last_index, length).replaceAll("\\\\\\.", ".")
+ members.reverse
+ }
+}
+
+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 9fa6619e9f..00e6f3769e 100644
--- a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala
@@ -19,7 +19,13 @@ import model.{ RootPackage => RootPackageEntity }
/** This trait extracts all required information for documentation from compilation units */
class ModelFactory(val global: Global, val settings: doc.Settings) {
- thisFactory: ModelFactory with ModelFactoryImplicitSupport with DiagramFactory with CommentFactory with TreeFactory =>
+ thisFactory: ModelFactory
+ with ModelFactoryImplicitSupport
+ with ModelFactoryTypeSupport
+ with DiagramFactory
+ with CommentFactory
+ with TreeFactory
+ with MemberLookup =>
import global._
import definitions.{ ObjectClass, NothingClass, AnyClass, AnyValClass, AnyRefClass }
@@ -32,7 +38,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
private var universe: Universe = null
private def dbg(msg: String) = if (sys.props contains "scala.scaladoc.debug") println(msg)
- private def closestPackage(sym: Symbol) = {
+ protected def closestPackage(sym: Symbol) = {
if (sym.isPackage || sym.isPackageClass) sym
else sym.enclosingPackage
}
@@ -63,7 +69,10 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
private val droppedPackages = mutable.Set[PackageImpl]()
protected val docTemplatesCache = new mutable.LinkedHashMap[Symbol, DocTemplateImpl]
protected val noDocTemplatesCache = new mutable.LinkedHashMap[Symbol, NoDocTemplateImpl]
- protected var typeCache = new mutable.LinkedHashMap[Type, TypeEntity]
+ def packageDropped(tpl: DocTemplateImpl) = tpl match {
+ case p: PackageImpl => droppedPackages(p)
+ case _ => false
+ }
def optimize(str: String): String =
if (str.length < 16) str.intern else str
@@ -83,6 +92,9 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
def toRoot: List[EntityImpl] = this :: inTpl.toRoot
def qualifiedName = name
def annotations = sym.annotations.map(makeAnnotation)
+ def inPackageObject: Boolean = sym.owner.isModuleClass && sym.owner.sourceModule.isPackageObject
+ def isType = sym.name.isTypeName
+ def isTerm = sym.name.isTermName
}
trait TemplateImpl extends EntityImpl with TemplateEntity {
@@ -94,13 +106,22 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
def isObject = sym.isModule && !sym.isPackage
def isCaseClass = sym.isCaseClass
def isRootPackage = false
- def ownType = makeType(sym.tpe, this)
+ def isNoDocMemberTemplate = false
def selfType = if (sym.thisSym eq sym) None else Some(makeType(sym.thisSym.typeOfThis, this))
- def inPackageObject: Boolean = sym.owner.isModuleClass && sym.owner.sourceModule.isPackageObject
}
- abstract class MemberImpl(sym: Symbol, implConv: ImplicitConversionImpl, inTpl: DocTemplateImpl) extends EntityImpl(sym, inTpl) with MemberEntity {
- lazy val comment = if (inTpl != null) thisFactory.comment(sym, inTpl) else None
+ abstract class MemberImpl(sym: Symbol, inTpl: DocTemplateImpl) extends EntityImpl(sym, inTpl) with MemberEntity {
+ lazy val comment = {
+ // If the current tpl is a DocTemplate, we consider itself as the root for resolving link targets (instead of the
+ // package the class is in) -- so people can refer to methods directly [[foo]], instead of using [[MyClass.foo]]
+ // in the doc comment of MyClass
+ val thisTpl = this match {
+ case d: DocTemplateImpl => Some(d)
+ case _ => None
+ }
+ if (inTpl != null) thisFactory.comment(sym, thisTpl, inTpl) else None
+ }
+ def group = if (comment.isDefined) comment.get.group.getOrElse("No Group") else "No Group"
override def inTemplate = inTpl
override def toRoot: List[MemberImpl] = this :: inTpl.toRoot
def inDefinitionTemplates = this match {
@@ -138,16 +159,16 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
* }}}
* the type the method returns is TraversableOps, which has all-abstract symbols. But in reality, it couldn't have
* any abstract terms, otherwise it would fail compilation. So we reset the DEFERRED flag. */
- if (!sym.isTrait && (sym hasFlag Flags.DEFERRED) && (implConv eq null)) fgs += Paragraph(Text("abstract"))
+ if (!sym.isTrait && (sym hasFlag Flags.DEFERRED) && (!isImplicitlyInherited)) fgs += Paragraph(Text("abstract"))
if (!sym.isModule && (sym hasFlag Flags.FINAL)) fgs += Paragraph(Text("final"))
fgs.toList
}
def deprecation =
if (sym.isDeprecated)
Some((sym.deprecationMessage, sym.deprecationVersion) match {
- case (Some(msg), Some(ver)) => parseWiki("''(Since version " + ver + ")'' " + msg, NoPosition)
- case (Some(msg), None) => parseWiki(msg, NoPosition)
- case (None, Some(ver)) => parseWiki("''(Since version " + ver + ")''", NoPosition)
+ case (Some(msg), Some(ver)) => parseWiki("''(Since version " + ver + ")'' " + msg, NoPosition, Some(inTpl))
+ case (Some(msg), None) => parseWiki(msg, NoPosition, Some(inTpl))
+ case (None, Some(ver)) => parseWiki("''(Since version " + ver + ")''", NoPosition, Some(inTpl))
case (None, None) => Body(Nil)
})
else
@@ -155,9 +176,9 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
def migration =
if(sym.hasMigrationAnnotation)
Some((sym.migrationMessage, sym.migrationVersion) match {
- case (Some(msg), Some(ver)) => parseWiki("''(Changed in version " + ver + ")'' " + msg, NoPosition)
- case (Some(msg), None) => parseWiki(msg, NoPosition)
- case (None, Some(ver)) => parseWiki("''(Changed in version " + ver + ")''", NoPosition)
+ case (Some(msg), Some(ver)) => parseWiki("''(Changed in version " + ver + ")'' " + msg, NoPosition, Some(inTpl))
+ case (Some(msg), None) => parseWiki(msg, NoPosition, Some(inTpl))
+ case (None, Some(ver)) => parseWiki("''(Changed in version " + ver + ")''", NoPosition, Some(inTpl))
case (None, None) => Body(Nil)
})
else
@@ -172,7 +193,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
case NullaryMethodType(res) => resultTpe(res)
case _ => tpe
}
- val tpe = if (implConv eq null) sym.tpe else implConv.toType memberInfo sym
+ val tpe = if (!isImplicitlyInherited) sym.tpe else byConversion.get.toType memberInfo sym
makeTypeInTemplateContext(resultTpe(tpe), inTemplate, sym)
}
def isDef = false
@@ -184,11 +205,43 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
def isAliasType = false
def isAbstractType = false
def isAbstract =
- // for the explanation of implConv == null see comment on flags
- ((!sym.isTrait && ((sym hasFlag Flags.ABSTRACT) || (sym hasFlag Flags.DEFERRED)) && (implConv == null)) ||
+ // for the explanation of conversion == null see comment on flags
+ ((!sym.isTrait && ((sym hasFlag Flags.ABSTRACT) || (sym hasFlag Flags.DEFERRED)) && (!isImplicitlyInherited)) ||
sym.isAbstractClass || sym.isAbstractType) && !sym.isSynthetic
def isTemplate = false
- def byConversion = if (implConv ne null) Some(implConv) else None
+ lazy val signature = {
+
+ def defParams(mbr: Any): String = mbr match {
+ case d: MemberEntity with Def =>
+ val paramLists: List[String] =
+ if (d.valueParams.isEmpty) Nil
+ else d.valueParams map (ps => ps map (_.resultType.name) mkString ("(",",",")"))
+ paramLists.mkString
+ case _ => ""
+ }
+
+ def tParams(mbr: Any): String = mbr match {
+ case hk: HigherKinded if !hk.typeParams.isEmpty =>
+ def boundsToString(hi: Option[TypeEntity], lo: Option[TypeEntity]): String = {
+ def bound0(bnd: Option[TypeEntity], pre: String): String = bnd match {
+ case None => ""
+ case Some(tpe) => pre ++ tpe.toString
+ }
+ bound0(hi, "<:") ++ bound0(lo, ">:")
+ }
+ "[" + hk.typeParams.map(tp => tp.variance + tp.name + tParams(tp) + boundsToString(tp.hi, tp.lo)).mkString(", ") + "]"
+ case _ => ""
+ }
+
+ (name + tParams(this) + defParams(this) +":"+ resultType.name).replaceAll("\\s","") // no spaces allowed, they break links
+ }
+ // these only apply for NonTemplateMemberEntities
+ def useCaseOf: Option[MemberEntity] = None
+ def byConversion: Option[ImplicitConversionImpl] = None
+ def isImplicitlyInherited = false
+ def isShadowedImplicit = false
+ def isAmbiguousImplicit = false
+ def isShadowedOrAmbiguousImplicit = false
}
/** A template that is not documented at all. The class is instantiated during lookups, to indicate that the class
@@ -198,26 +251,53 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
assert(modelFinished)
assert(!(noDocTemplatesCache isDefinedAt sym))
noDocTemplatesCache += (sym -> this)
-
def isDocTemplate = false
}
/** An inherited template that was not documented in its original owner - example:
* in classpath: trait T { class C } -- T (and implicitly C) are not documented
- * in the source: trait U extends T -- C appears in U as a NoDocTemplateMemberImpl -- that is, U has a member for it
+ * in the source: trait U extends T -- C appears in U as a MemberTemplateImpl -- that is, U has a member for it
* but C doesn't get its own page
*/
- class NoDocTemplateMemberImpl(sym: Symbol, inTpl: DocTemplateImpl) extends MemberImpl(sym, null, inTpl) with TemplateImpl with HigherKindedImpl with NoDocTemplateMemberEntity {
- assert(modelFinished)
-
+ abstract class MemberTemplateImpl(sym: Symbol, inTpl: DocTemplateImpl) extends MemberImpl(sym, inTpl) with TemplateImpl with HigherKindedImpl with MemberTemplateEntity {
+ // no templates cache for this class, each owner gets its own instance
+ override def isTemplate = true
def isDocTemplate = false
+ override def isNoDocMemberTemplate = true
lazy val definitionName = optimize(inDefinitionTemplates.head.qualifiedName + "." + name)
+ def valueParams: List[List[ValueParam]] = Nil /** TODO, these are now only computed for DocTemplates */
+
+ // Seems unused
+ // def parentTemplates =
+ // if (sym.isPackage || sym == AnyClass)
+ // List()
+ // else
+ // sym.tpe.parents.flatMap { tpe: Type =>
+ // val tSym = tpe.typeSymbol
+ // if (tSym != NoSymbol)
+ // List(makeTemplate(tSym))
+ // else
+ // List()
+ // } filter (_.isInstanceOf[DocTemplateEntity])
+
+ def parentTypes =
+ if (sym.isPackage || sym == AnyClass) List() else {
+ val tps = (this match {
+ case a: AliasType => sym.tpe.dealias.parents
+ case a: AbstractType => sym.info.bounds match {
+ case TypeBounds(lo, hi) => List(hi)
+ case _ => Nil
+ }
+ case _ => sym.tpe.parents
+ }) map { _.asSeenFrom(sym.thisType, sym) }
+ makeParentTypes(RefinedType(tps, EmptyScope), Some(this), inTpl)
+ }
}
/** The instantiation of `TemplateImpl` triggers the creation of the following entities:
* All ancestors of the template and all non-package members.
*/
- abstract class DocTemplateImpl(sym: Symbol, inTpl: DocTemplateImpl) extends MemberImpl(sym, null, inTpl) with TemplateImpl with HigherKindedImpl with DocTemplateEntity {
+ abstract class DocTemplateImpl(sym: Symbol, inTpl: DocTemplateImpl) extends MemberTemplateImpl(sym, inTpl) with DocTemplateEntity {
assert(!modelFinished)
assert(!(docTemplatesCache isDefinedAt sym), sym)
docTemplatesCache += (sym -> this)
@@ -253,24 +333,6 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
else None
}
- def parentTemplates =
- if (sym.isPackage || sym == AnyClass)
- List()
- else
- sym.tpe.parents.flatMap { tpe: Type =>
- val tSym = tpe.typeSymbol
- if (tSym != NoSymbol)
- List(makeTemplate(tSym))
- else
- List()
- } filter (_.isInstanceOf[DocTemplateEntity])
-
- def parentTypes =
- if (sym.isPackage || sym == AnyClass) List() else {
- val tps = sym.tpe.parents map { _.asSeenFrom(sym.thisType, sym) }
- makeParentTypes(RefinedType(tps, EmptyScope), Some(this), inTpl)
- }
-
protected def linearizationFromSymbol(symbol: Symbol): List[(TemplateEntity, TypeEntity)] = {
symbol.ancestors map { ancestor =>
val typeEntity = makeType(symbol.info.baseType(ancestor), this)
@@ -299,14 +361,14 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
def directSubClasses = allSubClasses.filter(_.parentTypes.map(_._1).contains(this))
/* Implcitly convertible class cache */
- private var implicitlyConvertibleClassesCache: mutable.ListBuffer[(DocTemplateEntity, ImplicitConversionImpl)] = null
- def registerImplicitlyConvertibleClass(dtpl: DocTemplateEntity, conv: ImplicitConversionImpl): Unit = {
+ private var implicitlyConvertibleClassesCache: mutable.ListBuffer[(DocTemplateImpl, ImplicitConversionImpl)] = null
+ def registerImplicitlyConvertibleClass(dtpl: DocTemplateImpl, conv: ImplicitConversionImpl): Unit = {
if (implicitlyConvertibleClassesCache == null)
- implicitlyConvertibleClassesCache = mutable.ListBuffer[(DocTemplateEntity, ImplicitConversionImpl)]()
+ implicitlyConvertibleClassesCache = mutable.ListBuffer[(DocTemplateImpl, ImplicitConversionImpl)]()
implicitlyConvertibleClassesCache += ((dtpl, conv))
}
- def incomingImplicitlyConvertedClasses: List[(DocTemplateEntity, ImplicitConversionImpl)] =
+ def incomingImplicitlyConvertedClasses: List[(DocTemplateImpl, ImplicitConversionImpl)] =
if (implicitlyConvertibleClassesCache == null)
List()
else
@@ -321,16 +383,16 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
lazy val memberSyms = sym.info.members.filter(s => membersShouldDocument(s, this))
// the inherited templates (classes, traits or objects)
- var memberSymsLazy = memberSyms.filter(t => templateShouldDocument(t, this) && !inOriginalOnwer(t, this))
+ var memberSymsLazy = memberSyms.filter(t => templateShouldDocument(t, this) && !inOriginalOwner(t, this))
// the direct members (methods, values, vars, types and directly contained templates)
var memberSymsEager = memberSyms.filter(!memberSymsLazy.contains(_))
// the members generated by the symbols in memberSymsEager
- val ownMembers = (memberSyms.flatMap(makeMember(_, null, this)))
+ val ownMembers = (memberSymsEager.flatMap(makeMember(_, None, this)))
// all the members that are documentented PLUS the members inherited by implicit conversions
var members: List[MemberImpl] = ownMembers
- def templates = members collect { case c: DocTemplateEntity => c }
+ def templates = members collect { case c: TemplateEntity with MemberEntity => c }
def methods = members collect { case d: Def => d }
def values = members collect { case v: Val => v }
def abstractTypes = members collect { case t: AbstractType => t }
@@ -342,13 +404,15 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
*/
def completeModel: Unit = {
// DFS completion
- for (member <- members)
- member match {
- case d: DocTemplateImpl => d.completeModel
- case _ =>
- }
+ // since alias types and abstract types have no own members, there's no reason for them to call completeModel
+ if (!sym.isAliasType && !sym.isAbstractType)
+ for (member <- members)
+ member match {
+ case d: DocTemplateImpl => d.completeModel
+ case _ =>
+ }
- members :::= memberSymsLazy.map(modelCreation.createLazyTemplateMember(_, inTpl))
+ members :::= memberSymsLazy.map(modelCreation.createLazyTemplateMember(_, this))
// compute linearization to register subclasses
linearization
@@ -369,7 +433,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
conv.targetTypeComponents map {
case pair@(template, tpe) =>
template match {
- case d: DocTemplateImpl => d.registerImplicitlyConvertibleClass(this, conv)
+ case d: DocTemplateImpl if (d != this) => d.registerImplicitlyConvertibleClass(this, conv)
case _ => // nothing
}
(pair._1, pair._2, conv)
@@ -378,21 +442,66 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
)
override def isTemplate = true
- lazy val definitionName = optimize(inDefinitionTemplates.head.qualifiedName + "." + name)
- def isDocTemplate = true
- def companion = sym.companionSymbol match {
- case NoSymbol => None
- case comSym if !isEmptyJavaObject(comSym) && (comSym.isClass || comSym.isModule) =>
- makeTemplate(comSym) match {
- case d: DocTemplateImpl => Some(d)
- case _ => None
+ override def isDocTemplate = true
+ private[this] lazy val companionSymbol =
+ if (sym.isAliasType || sym.isAbstractType) {
+ inTpl.sym.info.member(sym.name.toTermName) match {
+ case NoSymbol => NoSymbol
+ case s =>
+ s.info match {
+ case ot: OverloadedType =>
+ NoSymbol
+ case _ =>
+ // that's to navigate from val Foo: FooExtractor to FooExtractor :)
+ s.info.resultType.typeSymbol
+ }
}
- case _ => None
+ }
+ else
+ sym.companionSymbol
+
+ def companion =
+ companionSymbol match {
+ case NoSymbol => None
+ case comSym if !isEmptyJavaObject(comSym) && (comSym.isClass || comSym.isModule) =>
+ makeTemplate(comSym) match {
+ case d: DocTemplateImpl => Some(d)
+ case _ => None
+ }
+ case _ => None
+ }
+
+ def constructors: List[MemberImpl with Constructor] = if (isClass) members collect { case d: Constructor => d } else Nil
+ def primaryConstructor: Option[MemberImpl with Constructor] = if (isClass) constructors find { _.isPrimary } else None
+ override def valueParams =
+ // we don't want params on a class (non case class) signature
+ if (isCaseClass) primaryConstructor match {
+ case Some(const) => const.sym.paramss map (_ map (makeValueParam(_, this)))
+ case None => List()
+ }
+ else List.empty
+
+ // These are generated on-demand, make sure you don't call them more than once
+ def inheritanceDiagram = makeInheritanceDiagram(this)
+ def contentDiagram = makeContentDiagram(this)
+
+ def groupSearch[T](extractor: Comment => T, default: T): T = {
+ // query this template
+ if (comment.isDefined) {
+ val entity = extractor(comment.get)
+ if (entity != default) return entity
+ }
+ // query linearization
+ for (tpl <- linearizationTemplates.collect{ case dtpl: DocTemplateImpl if dtpl!=this => dtpl}) {
+ val entity = tpl.groupSearch(extractor, default)
+ if (entity != default) return entity
+ }
+ default
}
- // We make the diagram a lazy val, since we're not sure we'll include the diagrams in the page
- lazy val inheritanceDiagram = makeInheritanceDiagram(this)
- lazy val contentDiagram = makeContentDiagram(this)
+ def groupDescription(group: String): Option[Body] = groupSearch(_.groupDesc.get(group), None)
+ def groupPriority(group: String): Int = groupSearch(_.groupPrio.get(group) match { case Some(prio) => prio; case _ => 0 }, 0)
+ def groupName(group: String): String = groupSearch(_.groupNames.get(group) match { case Some(name) => name; case _ => group }, group)
}
abstract class PackageImpl(sym: Symbol, inTpl: PackageImpl) extends DocTemplateImpl(sym, inTpl) with Package {
@@ -409,18 +518,49 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
abstract class RootPackageImpl(sym: Symbol) extends PackageImpl(sym, null) with RootPackageEntity
- abstract class NonTemplateMemberImpl(sym: Symbol, implConv: ImplicitConversionImpl, inTpl: DocTemplateImpl) extends MemberImpl(sym, implConv, inTpl) with NonTemplateMemberEntity {
+ abstract class NonTemplateMemberImpl(sym: Symbol, conversion: Option[ImplicitConversionImpl],
+ override val useCaseOf: Option[MemberEntity], inTpl: DocTemplateImpl)
+ extends MemberImpl(sym, inTpl) with NonTemplateMemberEntity {
+ override lazy val comment = {
+ val inRealTpl =
+ /* Variable precendence order for implicitly added members: Take the variable defifinitions from ...
+ * 1. the target of the implicit conversion
+ * 2. the definition template (owner)
+ * 3. the current template
+ */
+ if (conversion.isDefined) findTemplateMaybe(conversion.get.toType.typeSymbol) match {
+ case Some(d) if d != makeRootPackage => d //in case of NoSymbol, it will give us the root package
+ case _ => findTemplateMaybe(sym.owner) match {
+ case Some(d) if d != makeRootPackage => d //in case of NoSymbol, it will give us the root package
+ case _ => inTpl
+ }
+ } else inTpl
+ if (inRealTpl != null) thisFactory.comment(sym, None, inRealTpl) else None
+ }
+
override def qualifiedName = optimize(inTemplate.qualifiedName + "#" + name)
- lazy val definitionName =
- if (implConv == null) optimize(inDefinitionTemplates.head.qualifiedName + "#" + name)
- else optimize(implConv.conversionQualifiedName + "#" + name)
- def isUseCase = sym.isSynthetic
+ lazy val definitionName = {
+ // this contrived name is here just to satisfy some older tests -- if you decide to remove it, be my guest, and
+ // also remove property("package object") from test/scaladoc/scalacheck/HtmlFactoryTest.scala so you don't break
+ // the test suite...
+ val packageObject = if (inPackageObject) ".package" else ""
+ if (!conversion.isDefined) optimize(inDefinitionTemplates.head.qualifiedName + packageObject + "#" + name)
+ else optimize(conversion.get.conversionQualifiedName + packageObject + "#" + name)
+ }
def isBridge = sym.isBridge
+ def isUseCase = useCaseOf.isDefined
+ override def byConversion: Option[ImplicitConversionImpl] = conversion
+ override def isImplicitlyInherited = { assert(modelFinished); conversion.isDefined }
+ override def isShadowedImplicit = isImplicitlyInherited && inTpl.implicitsShadowing.get(this).map(_.isShadowed).getOrElse(false)
+ override def isAmbiguousImplicit = isImplicitlyInherited && inTpl.implicitsShadowing.get(this).map(_.isAmbiguous).getOrElse(false)
+ override def isShadowedOrAmbiguousImplicit = isShadowedImplicit || isAmbiguousImplicit
}
- abstract class NonTemplateParamMemberImpl(sym: Symbol, implConv: ImplicitConversionImpl, inTpl: DocTemplateImpl) extends NonTemplateMemberImpl(sym, implConv, inTpl) {
+ abstract class NonTemplateParamMemberImpl(sym: Symbol, conversion: Option[ImplicitConversionImpl],
+ useCaseOf: Option[MemberEntity], inTpl: DocTemplateImpl)
+ extends NonTemplateMemberImpl(sym, conversion, useCaseOf, inTpl) {
def valueParams = {
- val info = if (implConv eq null) sym.info else implConv.toType memberInfo sym
+ val info = if (!isImplicitlyInherited) sym.info else conversion.get.toType memberInfo sym
info.paramss map { ps => (ps.zipWithIndex) map { case (p, i) =>
if (p.nameString contains "$") makeValueParam(p, inTpl, optimize("arg" + i)) else makeValueParam(p, inTpl)
}}
@@ -431,6 +571,12 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
val name = optimize(sym.nameString)
}
+ private trait AliasImpl {
+ def sym: Symbol
+ def inTpl: TemplateImpl
+ def alias = makeTypeInTemplateContext(sym.tpe.dealias, inTpl, sym)
+ }
+
private trait TypeBoundsImpl {
def sym: Symbol
def inTpl: TemplateImpl
@@ -454,14 +600,42 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
}
/* ============== MAKER METHODS ============== */
- /** */
+ /** This method makes it easier to work with the different kinds of symbols created by scalac by stripping down the
+ * package object abstraction and placing members directly in the package.
+ *
+ * Here's the explanation of what we do. The code:
+ *
+ * package foo {
+ * object `package` {
+ * class Bar
+ * }
+ * }
+ *
+ * will yield this Symbol structure:
+ * +---------+ (2)
+ * | |
+ * +---------------+ +---------- v ------- | ---+ +--------+ (2)
+ * | package foo#1 <---(1)---- module class foo#2 | | | |
+ * +---------------+ | +------------------ | -+ | +------------------- v ---+ |
+ * | | package object foo#3 <-----(1)---- module class package#4 | |
+ * | +----------------------+ | | +---------------------+ | |
+ * +--------------------------+ | | class package$Bar#5 | | |
+ * | +----------------- | -+ | |
+ * +------------------- | ---+ |
+ * | |
+ * +--------+
+ * (1) sourceModule
+ * (2) you get out of owners with .owner
+ *
+ * and normalizeTemplate(Bar.owner) will get us the package, instead of the module class of the package object.
+ */
def normalizeTemplate(aSym: Symbol): Symbol = aSym match {
case null | rootMirror.EmptyPackage | NoSymbol =>
normalizeTemplate(RootPackage)
case ObjectClass =>
normalizeTemplate(AnyRefClass)
case _ if aSym.isPackageObject =>
- aSym
+ normalizeTemplate(aSym.owner)
case _ if aSym.isModuleClass =>
normalizeTemplate(aSym.sourceModule)
case _ =>
@@ -477,13 +651,16 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
def createRootPackage: PackageImpl = docTemplatesCache.get(RootPackage) match {
case Some(root: PackageImpl) => root
- case _ => modelCreation.createTemplate(RootPackage, null).asInstanceOf[PackageImpl]
+ case _ => modelCreation.createTemplate(RootPackage, null) match {
+ case Some(root: PackageImpl) => root
+ case _ => sys.error("Scaladoc: Unable to create root package!")
+ }
}
/**
* Create a template, either a package, class, trait or object
*/
- def createTemplate(aSym: Symbol, inTpl: DocTemplateImpl): DocTemplateImpl = {
+ def createTemplate(aSym: Symbol, inTpl: DocTemplateImpl): Option[MemberImpl] = {
// don't call this after the model finished!
assert(!modelFinished)
@@ -493,7 +670,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
import Streamable._
Path(settings.docRootContent.value) match {
case f : File => {
- val rootComment = closing(f.inputStream)(is => parse(slurp(is), "", NoPosition))
+ val rootComment = closing(f.inputStream)(is => parse(slurp(is), "", NoPosition, Option(inTpl)))
Some(rootComment)
}
case _ => None
@@ -501,27 +678,24 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
}
def createDocTemplate(bSym: Symbol, inTpl: DocTemplateImpl): DocTemplateImpl = {
- if (bSym.isModule || (bSym.isAliasType && bSym.tpe.typeSymbol.isModule))
- new DocTemplateImpl(bSym, inTpl) with Object
- else if (bSym.isTrait || (bSym.isAliasType && bSym.tpe.typeSymbol.isTrait))
- new DocTemplateImpl(bSym, inTpl) with Trait
- else if (bSym.isClass || (bSym.isAliasType && bSym.tpe.typeSymbol.isClass))
- new DocTemplateImpl(bSym, inTpl) with Class {
- def valueParams =
- // we don't want params on a class (non case class) signature
- if (isCaseClass) List(sym.constrParamAccessors map (makeValueParam(_, this)))
- else List.empty
- val constructors =
- members collect { case d: Constructor => d }
- def primaryConstructor = constructors find { _.isPrimary }
- }
+ assert(!modelFinished) // only created BEFORE the model is finished
+ if (bSym.isAliasType && bSym != AnyRefClass)
+ new DocTemplateImpl(bSym, inTpl) with AliasImpl with AliasType { override def isAliasType = true }
+ else if (bSym.isAbstractType)
+ new DocTemplateImpl(bSym, inTpl) with TypeBoundsImpl with AbstractType { override def isAbstractType = true }
+ else if (bSym.isModule)
+ new DocTemplateImpl(bSym, inTpl) with Object {}
+ else if (bSym.isTrait)
+ new DocTemplateImpl(bSym, inTpl) with Trait {}
+ else if (bSym.isClass || bSym == AnyRefClass)
+ new DocTemplateImpl(bSym, inTpl) with Class {}
else
- sys.error("'" + bSym + "' isn't a class, trait or object thus cannot be built as a documentable template")
+ sys.error("'" + bSym + "' isn't a class, trait or object thus cannot be built as a documentable template.")
}
val bSym = normalizeTemplate(aSym)
if (docTemplatesCache isDefinedAt bSym)
- return docTemplatesCache(bSym)
+ return Some(docTemplatesCache(bSym))
/* Three cases of templates:
* (1) root package -- special cased for bootstrapping
@@ -529,7 +703,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
* (3) class/object/trait
*/
if (bSym == RootPackage) // (1)
- new RootPackageImpl(bSym) {
+ Some(new RootPackageImpl(bSym) {
override lazy val comment = createRootPackageComment
override val name = "root"
override def inTemplate = this
@@ -541,31 +715,52 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
(bSym.info.members ++ EmptyPackage.info.members) filter { s =>
s != EmptyPackage && s != RootPackage
}
- }
+ })
else if (bSym.isPackage) // (2)
- inTpl match {
- case inPkg: PackageImpl =>
- val pack = new PackageImpl(bSym, inPkg) {}
- if (pack.templates.isEmpty && pack.memberSymsLazy.isEmpty)
- droppedPackages += pack
- pack
- case _ =>
- sys.error("'" + bSym + "' must be in a package")
- }
+ if (settings.skipPackage(makeQualifiedName(bSym)))
+ None
+ else
+ inTpl match {
+ case inPkg: PackageImpl =>
+ val pack = new PackageImpl(bSym, inPkg) {}
+ // Used to check package pruning works:
+ //println(pack.qualifiedName)
+ if (pack.templates.filter(_.isDocTemplate).isEmpty && pack.memberSymsLazy.isEmpty) {
+ droppedPackages += pack
+ None
+ } else
+ Some(pack)
+ case _ =>
+ sys.error("'" + bSym + "' must be in a package")
+ }
else {
// no class inheritance at this point
- assert(inOriginalOnwer(bSym, inTpl))
- createDocTemplate(bSym, inTpl)
+ assert(inOriginalOwner(bSym, inTpl) || bSym.isAbstractType || bSym.isAliasType, bSym + " in " + inTpl)
+ Some(createDocTemplate(bSym, inTpl))
}
}
/**
* After the model is completed, no more DocTemplateEntities are created.
* Therefore any symbol that still appears is:
- * - NoDocTemplateMemberEntity (created here)
+ * - MemberTemplateEntity (created here)
* - NoDocTemplateEntity (created in makeTemplate)
*/
def createLazyTemplateMember(aSym: Symbol, inTpl: DocTemplateImpl): MemberImpl = {
+
+ // Code is duplicate because the anonymous classes are created statically
+ def createNoDocMemberTemplate(bSym: Symbol, inTpl: DocTemplateImpl): MemberTemplateImpl = {
+ assert(modelFinished) // only created AFTER the model is finished
+ if (bSym.isModule || (bSym.isAliasType && bSym.tpe.typeSymbol.isModule))
+ new MemberTemplateImpl(bSym, inTpl) with Object {}
+ else if (bSym.isTrait || (bSym.isAliasType && bSym.tpe.typeSymbol.isTrait))
+ new MemberTemplateImpl(bSym, inTpl) with Trait {}
+ else if (bSym.isClass || (bSym.isAliasType && bSym.tpe.typeSymbol.isClass))
+ new MemberTemplateImpl(bSym, inTpl) with Class {}
+ else
+ sys.error("'" + bSym + "' isn't a class, trait or object thus cannot be built as a member template.")
+ }
+
assert(modelFinished)
val bSym = normalizeTemplate(aSym)
@@ -579,7 +774,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
mbrs.head
case _ =>
// move the class completely to the new location
- new NoDocTemplateMemberImpl(aSym, inTpl)
+ createNoDocMemberTemplate(bSym, inTpl)
}
}
}
@@ -588,20 +783,18 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
def makeRootPackage: PackageImpl = docTemplatesCache(RootPackage).asInstanceOf[PackageImpl]
// TODO: Should be able to override the type
- def makeMember(aSym: Symbol, implConv: ImplicitConversionImpl, inTpl: DocTemplateImpl): List[MemberImpl] = {
+ def makeMember(aSym: Symbol, conversion: Option[ImplicitConversionImpl], inTpl: DocTemplateImpl): List[MemberImpl] = {
- def makeMember0(bSym: Symbol, _useCaseOf: Option[MemberImpl]): Option[MemberImpl] = {
+ def makeMember0(bSym: Symbol, useCaseOf: Option[MemberImpl]): Option[MemberImpl] = {
if (bSym.isGetter && bSym.isLazy)
- Some(new NonTemplateMemberImpl(bSym, implConv, inTpl) with Val {
+ Some(new NonTemplateMemberImpl(bSym, conversion, useCaseOf, inTpl) with Val {
override lazy val comment = // The analyser does not duplicate the lazy val's DocDef when it introduces its accessor.
- thisFactory.comment(bSym.accessed, inTpl.asInstanceOf[DocTemplateImpl]) // This hack should be removed after analyser is fixed.
+ thisFactory.comment(bSym.accessed, None, inTpl.asInstanceOf[DocTemplateImpl]) // This hack should be removed after analyser is fixed.
override def isLazyVal = true
- override def useCaseOf = _useCaseOf
})
else if (bSym.isGetter && bSym.accessed.isMutable)
- Some(new NonTemplateMemberImpl(bSym, implConv, inTpl) with Val {
+ Some(new NonTemplateMemberImpl(bSym, conversion, useCaseOf, inTpl) with Val {
override def isVar = true
- override def useCaseOf = _useCaseOf
})
else if (bSym.isMethod && !bSym.hasAccessorFlag && !bSym.isConstructor && !bSym.isModule) {
val cSym = { // This unsightly hack closes issue #4086.
@@ -615,45 +808,32 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
}
else bSym
}
- Some(new NonTemplateParamMemberImpl(cSym, implConv, inTpl) with HigherKindedImpl with Def {
+ Some(new NonTemplateParamMemberImpl(cSym, conversion, useCaseOf, inTpl) with HigherKindedImpl with Def {
override def isDef = true
- override def useCaseOf = _useCaseOf
})
}
- else if (bSym.isConstructor && (implConv == null))
- Some(new NonTemplateParamMemberImpl(bSym, implConv, inTpl) with Constructor {
- override def isConstructor = true
- def isPrimary = sym.isPrimaryConstructor
- override def useCaseOf = _useCaseOf
- })
+ else if (bSym.isConstructor)
+ if (conversion.isDefined)
+ None // don't list constructors inherted by implicit conversion
+ else
+ Some(new NonTemplateParamMemberImpl(bSym, conversion, useCaseOf, inTpl) with Constructor {
+ override def isConstructor = true
+ def isPrimary = sym.isPrimaryConstructor
+ })
else if (bSym.isGetter) // Scala field accessor or Java field
- Some(new NonTemplateMemberImpl(bSym, implConv, inTpl) with Val {
+ Some(new NonTemplateMemberImpl(bSym, conversion, useCaseOf, inTpl) with Val {
override def isVal = true
- override def useCaseOf = _useCaseOf
})
- else if (bSym.isAbstractType)
- Some(new NonTemplateMemberImpl(bSym, implConv, inTpl) with TypeBoundsImpl with HigherKindedImpl with AbstractType {
+ else if (bSym.isAbstractType && !typeShouldDocument(bSym, inTpl))
+ Some(new MemberTemplateImpl(bSym, inTpl) with TypeBoundsImpl with AbstractType {
override def isAbstractType = true
- override def useCaseOf = _useCaseOf
})
- else if (bSym.isAliasType && bSym != AnyRefClass)
- Some(new NonTemplateMemberImpl(bSym, implConv, inTpl) with HigherKindedImpl with AliasType {
+ else if (bSym.isAliasType && !typeShouldDocument(bSym, inTpl))
+ Some(new MemberTemplateImpl(bSym, inTpl) with AliasImpl with AliasType {
override def isAliasType = true
- def alias = makeTypeInTemplateContext(sym.tpe.dealias, inTpl, sym)
- override def useCaseOf = _useCaseOf
})
- else if (bSym.isPackage && !modelFinished)
- inTpl match {
- case inPkg: PackageImpl => modelCreation.createTemplate(bSym, inTpl) match {
- case p: PackageImpl if droppedPackages contains p => None
- case p: PackageImpl => Some(p)
- case _ => sys.error("'" + bSym + "' must be a package")
- }
- case _ =>
- sys.error("'" + bSym + "' must be in a package")
- }
- else if (!modelFinished && templateShouldDocument(bSym, inTpl) && inOriginalOnwer(bSym, inTpl))
- Some(modelCreation.createTemplate(bSym, inTpl))
+ else if (!modelFinished && (bSym.isPackage || bSym.isAliasType || bSym.isAbstractType || templateShouldDocument(bSym, inTpl)))
+ modelCreation.createTemplate(bSym, inTpl)
else
None
}
@@ -680,17 +860,20 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
inTpl.members.find(_.sym == aSym)
}
+ @deprecated("2.10", "Use findLinkTarget instead!")
def findTemplate(query: String): Option[DocTemplateImpl] = {
assert(modelFinished)
- docTemplatesCache.values find { (tpl: TemplateImpl) => tpl.qualifiedName == query && !tpl.isObject }
+ docTemplatesCache.values find { (tpl: DocTemplateImpl) => tpl.qualifiedName == query && !packageDropped(tpl) && !tpl.isObject }
}
def findTemplateMaybe(aSym: Symbol): Option[DocTemplateImpl] = {
assert(modelFinished)
- docTemplatesCache.get(normalizeTemplate(aSym))
+ docTemplatesCache.get(normalizeTemplate(aSym)).filterNot(packageDropped(_))
}
- def makeTemplate(aSym: Symbol): TemplateImpl = {
+ def makeTemplate(aSym: Symbol): TemplateImpl = makeTemplate(aSym, None)
+
+ def makeTemplate(aSym: Symbol, inTpl: Option[TemplateImpl]): TemplateImpl = {
assert(modelFinished)
def makeNoDocTemplate(aSym: Symbol, inTpl: TemplateImpl): NoDocTemplateImpl = {
@@ -706,11 +889,10 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
dtpl
case None =>
val bSym = normalizeTemplate(aSym)
- makeNoDocTemplate(bSym, makeTemplate(bSym.owner))
+ makeNoDocTemplate(bSym, if (inTpl.isDefined) inTpl.get else makeTemplate(bSym.owner))
}
}
-
/** */
def makeAnnotation(annot: AnnotationInfo): Annotation = {
val aSym = annot.symbol
@@ -720,7 +902,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
val arguments = { // lazy
def noParams = annot.args map { _ => None }
val params: List[Option[ValueParam]] = annotationClass match {
- case aClass: Class =>
+ case aClass: DocTemplateEntity with Class =>
(aClass.primaryConstructor map { _.valueParams.head }) match {
case Some(vps) => vps map { Some(_) }
case None => noParams
@@ -795,17 +977,37 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
}
/** Get the types of the parents of the current class, ignoring the refinements */
- def makeParentTypes(aType: Type, tpl: Option[DocTemplateImpl], inTpl: TemplateImpl): List[(TemplateEntity, TypeEntity)] = aType match {
+ def makeParentTypes(aType: Type, tpl: Option[MemberTemplateImpl], inTpl: TemplateImpl): List[(TemplateEntity, TypeEntity)] = aType match {
case RefinedType(parents, defs) =>
- val ignoreParents = Set[Symbol](AnyRefClass, ObjectClass)
+ val ignoreParents = Set[Symbol](AnyClass, AnyRefClass, ObjectClass)
val filtParents =
// we don't want to expose too many links to AnyRef, that will just be redundant information
- if (tpl.isDefined && (!tpl.get.isObject && parents.length < 2))
+ if (tpl.isDefined && { val sym = tpl.get.sym; (!sym.isModule && parents.length < 2) || (sym == AnyValClass) || (sym == AnyRefClass) || (sym == AnyClass) })
parents
else
parents.filterNot((p: Type) => ignoreParents(p.typeSymbol))
+
+ /** Returns:
+ * - a DocTemplate if the type's symbol is documented
+ * - a NoDocTemplateMember if the type's symbol is not documented in its parent but in another template
+ * - a NoDocTemplate if the type's symbol is not documented at all */
+ def makeTemplateOrMemberTemplate(parent: Type): TemplateImpl = {
+ def noDocTemplate = makeTemplate(parent.typeSymbol)
+ findTemplateMaybe(parent.typeSymbol) match {
+ case Some(tpl) => tpl
+ case None => parent match {
+ case TypeRef(pre, sym, args) =>
+ findTemplateMaybe(pre.typeSymbol) match {
+ case Some(tpl) => findMember(parent.typeSymbol, tpl).collect({case t: TemplateImpl => t}).getOrElse(noDocTemplate)
+ case None => noDocTemplate
+ }
+ case _ => noDocTemplate
+ }
+ }
+ }
+
filtParents.map(parent => {
- val templateEntity = makeTemplate(parent.typeSymbol)
+ val templateEntity = makeTemplateOrMemberTemplate(parent)
val typeEntity = makeType(parent, inTpl)
(templateEntity, typeEntity)
})
@@ -813,160 +1015,38 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
List((makeTemplate(aType.typeSymbol), makeType(aType, inTpl)))
}
- /** */
- def makeType(aType: Type, inTpl: TemplateImpl): TypeEntity = {
- def templatePackage = closestPackage(inTpl.sym)
-
- def createTypeEntity = new TypeEntity {
- private val nameBuffer = new StringBuilder
- private var refBuffer = new immutable.TreeMap[Int, (TemplateEntity, Int)]
- private def appendTypes0(types: List[Type], sep: String): Unit = types match {
- case Nil =>
- case tp :: Nil =>
- appendType0(tp)
- case tp :: tps =>
- appendType0(tp)
- nameBuffer append sep
- appendTypes0(tps, sep)
- }
-
- private def appendType0(tpe: Type): Unit = tpe match {
- /* Type refs */
- case tp: TypeRef if definitions.isFunctionType(tp) =>
- val args = tp.normalize.typeArgs
- nameBuffer append '('
- appendTypes0(args.init, ", ")
- nameBuffer append ") ⇒ "
- appendType0(args.last)
- case tp: TypeRef if definitions.isScalaRepeatedParamType(tp) =>
- appendType0(tp.args.head)
- nameBuffer append '*'
- case tp: TypeRef if definitions.isByNameParamType(tp) =>
- nameBuffer append "⇒ "
- appendType0(tp.args.head)
- case tp: TypeRef if definitions.isTupleType(tp) =>
- val args = tp.normalize.typeArgs
- nameBuffer append '('
- appendTypes0(args, ", ")
- nameBuffer append ')'
- case TypeRef(pre, aSym, targs) =>
- val preSym = pre.widen.typeSymbol
- // There's a work in progress here trying to deal with the
- // places where undesirable prefixes are printed.
- // ...
- // If the prefix is something worthy of printing, see if the prefix type
- // is in the same package as the enclosing template. If so, print it
- // unqualified and they'll figure it out.
- //
- // val stripPrefixes = List(templatePackage.fullName + ".", "package.", "java.lang.")
- // if (!preSym.printWithoutPrefix) {
- // nameBuffer append stripPrefixes.foldLeft(pre.prefixString)(_ stripPrefix _)
- // }
- val bSym = normalizeTemplate(aSym)
- if (bSym.isNonClassType && bSym != AnyRefClass) {
- nameBuffer append bSym.decodedName
- } else {
- val tpl = makeTemplate(bSym)
- val pos0 = nameBuffer.length
- refBuffer += pos0 -> (tpl, tpl.name.length)
- nameBuffer append tpl.name
- }
- if (!targs.isEmpty) {
- nameBuffer append '['
- appendTypes0(targs, ", ")
- nameBuffer append ']'
- }
- /* Refined types */
- case RefinedType(parents, defs) =>
- val ignoreParents = Set[Symbol](AnyClass, ObjectClass)
- val filtParents = parents filterNot (x => ignoreParents(x.typeSymbol)) match {
- case Nil => parents
- case ps => ps
- }
- appendTypes0(filtParents, " with ")
- // XXX Still todo: properly printing refinements.
- // Since I didn't know how to go about displaying a multi-line type, I went with
- // printing single method refinements (which should be the most common) and printing
- // the number of members if there are more.
- defs.toList match {
- case Nil => ()
- case x :: Nil => nameBuffer append (" { " + x.defString + " }")
- case xs => nameBuffer append (" { ... /* %d definitions in type refinement */ }" format xs.size)
- }
- /* Eval-by-name types */
- case NullaryMethodType(result) =>
- nameBuffer append '⇒'
- appendType0(result)
- /* Polymorphic types */
- case PolyType(tparams, result) => assert(tparams.nonEmpty)
-// throw new Error("Polymorphic type '" + tpe + "' cannot be printed as a type")
- def typeParamsToString(tps: List[Symbol]): String = if (tps.isEmpty) "" else
- tps.map{tparam =>
- tparam.varianceString + tparam.name + typeParamsToString(tparam.typeParams)
- }.mkString("[", ", ", "]")
- nameBuffer append typeParamsToString(tparams)
- appendType0(result)
- case tpen =>
- nameBuffer append tpen.toString
+ def makeQualifiedName(sym: Symbol, relativeTo: Option[Symbol] = None): String = {
+ val stop = if (relativeTo.isDefined) relativeTo.get.ownerChain.toSet else Set[Symbol]()
+ var sym1 = sym
+ var path = new StringBuilder()
+ // var path = List[Symbol]()
+
+ while ((sym1 != NoSymbol) && (path.isEmpty || !stop(sym1))) {
+ val sym1Norm = normalizeTemplate(sym1)
+ if (!sym1.sourceModule.isPackageObject && sym1Norm != RootPackage) {
+ if (path.length != 0)
+ path.insert(0, ".")
+ path.insert(0, sym1Norm.nameString)
+ // path::= sym1Norm
}
- appendType0(aType)
- val refEntity = refBuffer
- val name = optimize(nameBuffer.toString)
+ sym1 = sym1.owner
}
- if (aType.isTrivial)
- typeCache.get(aType) match {
- case Some(typeEntity) => typeEntity
- case None =>
- val typeEntity = createTypeEntity
- typeCache += aType -> typeEntity
- typeEntity
- }
- else
- createTypeEntity
+ optimize(path.toString)
+ //path.mkString(".")
}
- def normalizeOwner(aSym: Symbol): Symbol =
- /*
- * Okay, here's the explanation of what happens. The code:
- *
- * package foo {
- * object `package` {
- * class Bar
- * }
- * }
- *
- * will yield this Symbol structure:
- *
- * +---------------+ +--------------------------+
- * | package foo#1 ----(1)---> module class foo#2 |
- * +---------------+ | +----------------------+ | +-------------------------+
- * | | package object foo#3 ------(1)---> module class package#4 |
- * | +----------------------+ | | +---------------------+ |
- * +--------------------------+ | | class package$Bar#5 | |
- * | +---------------------+ |
- * +-------------------------+
- * (1) sourceModule
- * (2) you get out of owners with .owner
- */
- normalizeTemplate(aSym) match {
- case bSym if bSym.isPackageObject =>
- normalizeOwner(bSym.owner)
- case bSym =>
- bSym
- }
-
- def inOriginalOnwer(aSym: Symbol, inTpl: TemplateImpl): Boolean =
- normalizeOwner(aSym.owner) == normalizeOwner(inTpl.sym)
+ def inOriginalOwner(aSym: Symbol, inTpl: TemplateImpl): Boolean =
+ normalizeTemplate(aSym.owner) == normalizeTemplate(inTpl.sym)
def templateShouldDocument(aSym: Symbol, inTpl: TemplateImpl): Boolean =
- (aSym.isClass || aSym.isModule || aSym == AnyRefClass) &&
+ (aSym.isTrait || aSym.isClass || aSym.isModule) &&
localShouldDocument(aSym) &&
!isEmptyJavaObject(aSym) &&
// either it's inside the original owner or we can document it later:
- (!inOriginalOnwer(aSym, inTpl) || (aSym.isPackageClass || (aSym.sourceFile != null)))
+ (!inOriginalOwner(aSym, inTpl) || (aSym.isPackageClass || (aSym.sourceFile != null)))
- def membersShouldDocument(sym: Symbol, inTpl: TemplateImpl) =
+ def membersShouldDocument(sym: Symbol, inTpl: TemplateImpl) = {
// pruning modules that shouldn't be documented
// Why Symbol.isInitialized? Well, because we need to avoid exploring all the space available to scaladoc
// from the classpath -- scaladoc is a hog, it will explore everything starting from the root package unless we
@@ -979,6 +1059,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
(!sym.isConstructor || sym.owner == inTpl.sym) &&
// If the @bridge annotation overrides a normal member, show it
!isPureBridge(sym)
+ }
def isEmptyJavaObject(aSym: Symbol): Boolean =
aSym.isModule && aSym.isJavaDefined &&
@@ -995,5 +1076,11 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
// the implicit conversions that are excluded from the pages should not appear in the diagram
def implicitExcluded(convertorMethod: String): Boolean = settings.hardcoded.commonConversionTargets.contains(convertorMethod)
+
+ // whether or not to create a page for an {abstract,alias} type
+ def typeShouldDocument(bSym: Symbol, inTpl: DocTemplateImpl) =
+ (settings.docExpandAllTypes.value && (bSym.sourceFile != null)) ||
+ { val rawComment = global.expandedDocComment(bSym, inTpl.sym)
+ rawComment.contains("@template") || rawComment.contains("@documentable") }
}
diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala
index 8cbf2ac1b6..00254df7ef 100644
--- a/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala
@@ -53,7 +53,7 @@ import model.{ RootPackage => RootPackageEntity }
* TODO: Give an overview here
*/
trait ModelFactoryImplicitSupport {
- thisFactory: ModelFactory with CommentFactory with TreeFactory =>
+ thisFactory: ModelFactory with ModelFactoryTypeSupport with CommentFactory with TreeFactory =>
import global._
import global.analyzer._
@@ -115,7 +115,7 @@ trait ModelFactoryImplicitSupport {
// Put the class-specific conversions in front
val (ownConversions, commonConversions) =
- conversions.partition(conv => !hardcoded.commonConversionTargets.contains(conv.conversionQualifiedName))
+ conversions.partition(!_.isCommonConversion)
ownConversions ::: commonConversions
}
@@ -329,11 +329,6 @@ trait ModelFactoryImplicitSupport {
}
}
- def makeQualifiedName(sym: Symbol): String = {
- val remove = Set[Symbol](RootPackage, RootClass, EmptyPackage, EmptyPackageClass)
- sym.ownerChain.filterNot(remove.contains(_)).reverse.map(_.nameString).mkString(".")
- }
-
/* ============== IMPLEMENTATION PROVIDING ENTITY TYPES ============== */
class ImplicitConversionImpl(
@@ -352,7 +347,7 @@ trait ModelFactoryImplicitSupport {
if (convSym != NoSymbol)
makeTemplate(convSym.owner)
else {
- error("Scaladoc implicits: Implicit conversion from " + sym.tpe + " to " + toType + " done by " + convSym + " = NoSymbol!")
+ error("Scaladoc implicits: " + toString + " = NoSymbol!")
makeRootPackage
}
@@ -408,29 +403,22 @@ trait ModelFactoryImplicitSupport {
debug("")
memberSyms.flatMap({ aSym =>
- makeTemplate(aSym.owner) match {
- case d: DocTemplateImpl =>
- // we can't just pick up nodes from the previous template, although that would be very convenient:
- // they need the byConversion field to be attached to themselves -- this is design decision I should
- // revisit soon
- //
- // d.ownMembers.collect({
- // // it's either a member or has a couple of usecases it's hidden behind
- // case m: MemberImpl if m.sym == aSym =>
- // m // the member itself
- // case m: MemberImpl if m.useCaseOf.isDefined && m.useCaseOf.get.asInstanceOf[MemberImpl].sym == aSym =>
- // m.useCaseOf.get.asInstanceOf[MemberImpl] // the usecase
- // })
- makeMember(aSym, this, d)
- case _ =>
- // should only happen if the code for this template is not part of the scaladoc run =>
- // members won't have any comments
- makeMember(aSym, this, inTpl)
- }
+ // we can't just pick up nodes from the original template, although that would be very convenient:
+ // they need the byConversion field to be attached to themselves and the types to be transformed by
+ // asSeenFrom
+
+ // at the same time, the member itself is in the inTpl, not in the new template -- but should pick up
+ // variables from the old template. Ugly huh? We'll always create the member inTpl, but it will change
+ // the template when expanding variables in the comment :)
+ makeMember(aSym, Some(this), inTpl)
})
}
lazy val members: List[MemberEntity] = memberImpls
+
+ def isCommonConversion = hardcoded.commonConversionTargets.contains(conversionQualifiedName)
+
+ override def toString = "Implcit conversion from " + sym.tpe + " to " + toType + " done by " + convSym
}
/* ========================= HELPER METHODS ========================== */
diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactoryTypeSupport.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryTypeSupport.scala
new file mode 100644
index 0000000000..d2a26d1309
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryTypeSupport.scala
@@ -0,0 +1,323 @@
+/* NSC -- new Scala compiler -- Copyright 2007-2011 LAMP/EPFL */
+
+package scala.tools.nsc
+package doc
+package model
+
+import comment._
+
+import diagram._
+
+import scala.collection._
+import scala.util.matching.Regex
+
+import symtab.Flags
+
+import io._
+
+import model.{ RootPackage => RootPackageEntity }
+
+/** This trait extracts all required information for documentation from compilation units */
+trait ModelFactoryTypeSupport {
+ thisFactory: ModelFactory
+ with ModelFactoryImplicitSupport
+ with ModelFactoryTypeSupport
+ with DiagramFactory
+ with CommentFactory
+ with TreeFactory =>
+
+ import global._
+ import definitions.{ ObjectClass, NothingClass, AnyClass, AnyValClass, AnyRefClass }
+ import rootMirror.{ RootPackage, RootClass, EmptyPackage }
+
+ protected var typeCache = new mutable.LinkedHashMap[Type, TypeEntity]
+
+ /** */
+ def makeType(aType: Type, inTpl: TemplateImpl): TypeEntity = {
+ def templatePackage = closestPackage(inTpl.sym)
+
+ def createTypeEntity = new TypeEntity {
+ private var nameBuffer = new StringBuilder
+ private var refBuffer = new immutable.TreeMap[Int, (LinkTo, Int)]
+ private def appendTypes0(types: List[Type], sep: String): Unit = types match {
+ case Nil =>
+ case tp :: Nil =>
+ appendType0(tp)
+ case tp :: tps =>
+ appendType0(tp)
+ nameBuffer append sep
+ appendTypes0(tps, sep)
+ }
+
+ private def appendType0(tpe: Type): Unit = tpe match {
+ /* Type refs */
+ case tp: TypeRef if definitions.isFunctionType(tp) =>
+ val args = tp.normalize.typeArgs
+ nameBuffer append '('
+ appendTypes0(args.init, ", ")
+ nameBuffer append ") ⇒ "
+ appendType0(args.last)
+ case tp: TypeRef if definitions.isScalaRepeatedParamType(tp) =>
+ appendType0(tp.args.head)
+ nameBuffer append '*'
+ case tp: TypeRef if definitions.isByNameParamType(tp) =>
+ nameBuffer append "⇒ "
+ appendType0(tp.args.head)
+ case tp: TypeRef if definitions.isTupleType(tp) =>
+ val args = tp.normalize.typeArgs
+ nameBuffer append '('
+ appendTypes0(args, ", ")
+ nameBuffer append ')'
+ case TypeRef(pre, aSym, targs) =>
+ val preSym = pre.widen.typeSymbol
+
+ // SI-3314/SI-4888: Classes, Traits and Types can be inherited from a template to another:
+ // class Enum { abstract class Value }
+ // class Day extends Enum { object Mon extends Value /*...*/ }
+ // ===> 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
+ val bSym = normalizeTemplate(aSym)
+ val owner =
+ if ((preSym != NoSymbol) && /* it needs a prefix */
+ (preSym != bSym.owner) && /* prefix is different from owner */
+ (aSym == bSym)) /* normalization doesn't play tricks on us */
+ preSym
+ 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))
+ }
+
+ // SI-4360 Showing prefixes when necessary
+ // We check whether there's any directly accessible type with the same name in the current template OR if the
+ // type is inherited from one template to another. There may be multiple symbols with the same name in scope,
+ // but we won't show the prefix if our symbol is among them, only if *it's not* -- that's equal to showing
+ // the prefix only for ambiguous references, not for overloaded ones.
+ def needsPrefix: Boolean = {
+ if ((owner != bSym.owner || preSym.isRefinementClass) && (normalizeTemplate(owner) != inTpl.sym))
+ return true
+ // don't get tricked into prefixng method type params and existentials:
+ // I tried several tricks BUT adding the method for which I'm creating the type => that simply won't scale,
+ // as ValueParams are independent of their parent member, and I really don't want to add this information to
+ // all terms, as we're already over the allowed memory footprint
+ if (aSym.isTypeParameterOrSkolem || aSym.isExistentiallyBound /* existential or existential skolem */)
+ return false
+
+ for (tpl <- inTpl.sym.ownerChain) {
+ tpl.info.member(bSym.name) match {
+ case NoSymbol =>
+ // No syms with that name, look further inside the owner chain
+ case sym =>
+ // Symbol found -- either the correct symbol, another one OR an overloaded alternative
+ if (sym == bSym)
+ return false
+ else sym.info match {
+ case OverloadedType(owner, alternatives) =>
+ return alternatives.contains(bSym)
+ case _ =>
+ return true
+ }
+ }
+ }
+ // if it's not found in the owner chain, we can safely leave out the prefix
+ false
+ }
+
+ val prefix =
+ if (!settings.docNoPrefixes.value && needsPrefix && (bSym != AnyRefClass /* which we normalize */)) {
+ if (!owner.isRefinementClass) {
+ val qName = makeQualifiedName(owner, Some(inTpl.sym))
+ if (qName != "") qName + "." else ""
+ }
+ else {
+ nameBuffer append "("
+ appendType0(pre)
+ nameBuffer append ")#"
+ "" // we already appended the prefix
+ }
+ } else ""
+
+ //DEBUGGING:
+ //if (makeQualifiedName(bSym) == "pack1.A") println("needsPrefix(" + bSym + ", " + owner + ", " + inTpl.qualifiedName + ") => " + needsPrefix + " and prefix=" + prefix)
+
+ val name = prefix + bSym.nameString
+ val pos0 = nameBuffer.length
+ refBuffer += pos0 -> ((link, name.length))
+ nameBuffer append name
+
+ if (!targs.isEmpty) {
+ nameBuffer append '['
+ appendTypes0(targs, ", ")
+ nameBuffer append ']'
+ }
+ /* Refined types */
+ case RefinedType(parents, defs) =>
+ val ignoreParents = Set[Symbol](AnyClass, ObjectClass)
+ val filtParents = parents filterNot (x => ignoreParents(x.typeSymbol)) match {
+ case Nil => parents
+ case ps => ps
+ }
+ appendTypes0(filtParents, " with ")
+ // XXX Still todo: properly printing refinements.
+ // Since I didn't know how to go about displaying a multi-line type, I went with
+ // printing single method refinements (which should be the most common) and printing
+ // the number of members if there are more.
+ defs.toList match {
+ case Nil => ()
+ case x :: Nil => nameBuffer append (" { " + x.defString + " }")
+ case xs => nameBuffer append (" { ... /* %d definitions in type refinement */ }" format xs.size)
+ }
+ /* Eval-by-name types */
+ case NullaryMethodType(result) =>
+ nameBuffer append '⇒'
+ appendType0(result)
+
+ /* Polymorphic types */
+ case PolyType(tparams, result) => assert(tparams.nonEmpty)
+ def typeParamsToString(tps: List[Symbol]): String = if (tps.isEmpty) "" else
+ tps.map{tparam =>
+ tparam.varianceString + tparam.name + typeParamsToString(tparam.typeParams)
+ }.mkString("[", ", ", "]")
+ nameBuffer append typeParamsToString(tparams)
+ appendType0(result)
+
+ case et@ExistentialType(quantified, underlying) =>
+
+ def appendInfoStringReduced(sym: Symbol, tp: Type): Unit = {
+ if (sym.isType && !sym.isAliasType && !sym.isClass) {
+ tp match {
+ case PolyType(tparams, _) =>
+ nameBuffer append "["
+ appendTypes0(tparams.map(_.tpe), ", ")
+ nameBuffer append "]"
+ case _ =>
+ }
+ tp.resultType match {
+ case rt @ TypeBounds(_, _) =>
+ appendType0(rt)
+ case rt =>
+ nameBuffer append " <: "
+ appendType0(rt)
+ }
+ } else {
+ // fallback to the Symbol infoString
+ nameBuffer append sym.infoString(tp)
+ }
+ }
+
+ def appendClauses = {
+ nameBuffer append " forSome {"
+ var first = true
+ val qset = quantified.toSet
+ for (sym <- quantified) {
+ if (!first) { nameBuffer append ", " } else first = false
+ if (sym.isSingletonExistential) {
+ nameBuffer append "val "
+ nameBuffer append tpnme.dropSingletonName(sym.name)
+ nameBuffer append ": "
+ appendType0(dropSingletonType(sym.info.bounds.hi))
+ } else {
+ if (sym.flagString != "") nameBuffer append (sym.flagString + " ")
+ if (sym.keyString != "") nameBuffer append (sym.keyString + " ")
+ nameBuffer append sym.varianceString
+ nameBuffer append sym.nameString
+ appendInfoStringReduced(sym, sym.info)
+ }
+ }
+ nameBuffer append "}"
+ }
+
+ underlying match {
+ case TypeRef(pre, sym, args) if et.isRepresentableWithWildcards =>
+ appendType0(typeRef(pre, sym, Nil))
+ nameBuffer append "["
+ var first = true
+ val qset = quantified.toSet
+ for (arg <- args) {
+ if (!first) { nameBuffer append ", " } else first = false
+ arg match {
+ case TypeRef(_, sym, _) if (qset contains sym) =>
+ nameBuffer append "_"
+ appendInfoStringReduced(sym, sym.info)
+ case arg =>
+ appendType0(arg)
+ }
+ }
+ nameBuffer append "]"
+ case MethodType(_, _) | NullaryMethodType(_) | PolyType(_, _) =>
+ nameBuffer append "("
+ appendType0(underlying)
+ nameBuffer append ")"
+ appendClauses
+ case _ =>
+ appendType0(underlying)
+ appendClauses
+ }
+
+ case tb@TypeBounds(lo, hi) =>
+ if (tb.lo != TypeBounds.empty.lo) {
+ nameBuffer append " >: "
+ appendType0(lo)
+ }
+ if (tb.hi != TypeBounds.empty.hi) {
+ nameBuffer append " <: "
+ appendType0(hi)
+ }
+ // case tpen: ThisType | SingleType | SuperType =>
+ // if (tpen.isInstanceOf[ThisType] && tpen.asInstanceOf[ThisType].sym.isEffectiveRoot) {
+ // appendType0 typeRef(NoPrefix, sym, Nil)
+ // } else {
+ // val underlying =
+ // val pre = underlying.typeSymbol.skipPackageObject
+ // if (pre.isOmittablePrefix) pre.fullName + ".type"
+ // else prefixString + "type"
+ case tpen@ThisType(sym) =>
+ appendType0(typeRef(NoPrefix, sym, Nil))
+ nameBuffer append ".this"
+ if (!tpen.underlying.typeSymbol.skipPackageObject.isOmittablePrefix) nameBuffer append ".type"
+ case tpen@SuperType(thistpe, supertpe) =>
+ nameBuffer append "super["
+ appendType0(supertpe)
+ nameBuffer append "]"
+ case tpen@SingleType(pre, sym) =>
+ appendType0(typeRef(pre, sym, Nil))
+ if (!tpen.underlying.typeSymbol.skipPackageObject.isOmittablePrefix) nameBuffer append ".type"
+ case tpen =>
+ nameBuffer append tpen.toString
+ }
+ appendType0(aType)
+ val refEntity = refBuffer
+ val name = optimize(nameBuffer.toString)
+ nameBuffer = null
+ }
+
+ // 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
+ }
+} \ No newline at end of file
diff --git a/src/compiler/scala/tools/nsc/doc/model/TypeEntity.scala b/src/compiler/scala/tools/nsc/doc/model/TypeEntity.scala
index 67e955f613..643067cca5 100644
--- a/src/compiler/scala/tools/nsc/doc/model/TypeEntity.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/TypeEntity.scala
@@ -9,7 +9,6 @@ package model
import scala.collection._
-
/** A type. Note that types and templates contain the same information only for the simplest types. For example, a type
* defines how a template's type parameters are instantiated (as in `List[Cow]`), what the template's prefix is
* (as in `johnsFarm.Cow`), and supports compound or structural types. */
@@ -21,9 +20,8 @@ abstract class TypeEntity {
/** Maps which parts of this type's name reference entities. The map is indexed by the position of the first
* character that reference some entity, and contains the entity and the position of the last referenced
* character. The referenced character ranges do not to overlap or nest. The map is sorted by position. */
- def refEntity: SortedMap[Int, (TemplateEntity, Int)]
+ def refEntity: SortedMap[Int, (LinkTo, Int)]
/** The human-readable representation of this type. */
override def toString = name
-
}
diff --git a/src/compiler/scala/tools/nsc/doc/model/comment/Body.scala b/src/compiler/scala/tools/nsc/doc/model/comment/Body.scala
index ecc3273903..3f0024cb68 100644
--- a/src/compiler/scala/tools/nsc/doc/model/comment/Body.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/comment/Body.scala
@@ -44,7 +44,6 @@ final case class Body(blocks: Seq[Block]) {
case inlines => Some(Chain(inlines))
}
}
-
}
/** A block-level element of text, such as a paragraph or code block. */
@@ -67,10 +66,14 @@ final case class Bold(text: Inline) extends Inline
final case class Underline(text: Inline) extends Inline
final case class Superscript(text: Inline) extends Inline
final case class Subscript(text: Inline) extends Inline
-final case class EntityLink(target: String, template: () => Option[TemplateEntity]) extends Inline
final case class Link(target: String, title: Inline) extends Inline
final case class Monospace(text: Inline) extends Inline
final case class Text(text: String) extends Inline
+abstract class EntityLink(val title: Inline) extends Inline { def link: LinkTo }
+object EntityLink {
+ def apply(title: Inline, linkTo: LinkTo) = new EntityLink(title) { def link: LinkTo = linkTo }
+ def unapply(el: EntityLink): Option[(Inline, LinkTo)] = Some((el.title, el.link))
+}
final case class HtmlTag(data: String) extends Inline {
def canClose(open: HtmlTag) = {
open.data.stripPrefix("<") == data.stripPrefix("</")
diff --git a/src/compiler/scala/tools/nsc/doc/model/comment/Comment.scala b/src/compiler/scala/tools/nsc/doc/model/comment/Comment.scala
index 7b70683db5..c8f4c2f285 100644
--- a/src/compiler/scala/tools/nsc/doc/model/comment/Comment.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/comment/Comment.scala
@@ -114,6 +114,18 @@ abstract class Comment {
/** A set of diagram directives for the content diagram */
def contentDiagram: List[String]
+ /** The group this member is part of */
+ def group: Option[String]
+
+ /** Member group descriptions */
+ def groupDesc: Map[String,Body]
+
+ /** Member group names (overriding the short tag) */
+ def groupNames: Map[String,String]
+
+ /** Member group priorities */
+ def groupPrio: Map[String,Int]
+
override def toString =
body.toString + "\n" +
(authors map ("@author " + _.toString)).mkString("\n") +
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 2099315cc6..1a11964e37 100644
--- a/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala
@@ -23,7 +23,7 @@ import language.postfixOps
*
* @author Manohar Jonnalagedda
* @author Gilles Dubochet */
-trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
+trait CommentFactory { thisFactory: ModelFactory with CommentFactory with MemberLookup=>
val global: Global
import global.{ reporter, definitions }
@@ -31,16 +31,16 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
protected val commentCache = mutable.HashMap.empty[(global.Symbol, TemplateImpl), Comment]
def addCommentBody(sym: global.Symbol, inTpl: TemplateImpl, docStr: String, docPos: global.Position): global.Symbol = {
- commentCache += (sym, inTpl) -> parse(docStr, docStr, docPos)
+ commentCache += (sym, inTpl) -> parse(docStr, docStr, docPos, None)
sym
}
- def comment(sym: global.Symbol, inTpl: DocTemplateImpl): Option[Comment] = {
+ def comment(sym: global.Symbol, currentTpl: Option[DocTemplateImpl], inTpl: DocTemplateImpl): Option[Comment] = {
val key = (sym, inTpl)
if (commentCache isDefinedAt key)
Some(commentCache(key))
else {
- val c = defineComment(sym, inTpl)
+ val c = defineComment(sym, currentTpl, inTpl)
if (c isDefined) commentCache += (sym, inTpl) -> c.get
c
}
@@ -50,7 +50,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
* cases we have to give some `inTpl` comments (parent class for example)
* to the comment of the symbol.
* This function manages some of those cases : Param accessor and Primary constructor */
- def defineComment(sym: global.Symbol, inTpl: DocTemplateImpl):Option[Comment] = {
+ def defineComment(sym: global.Symbol, currentTpl: Option[DocTemplateImpl], inTpl: DocTemplateImpl):Option[Comment] = {
//param accessor case
// We just need the @param argument, we put it into the body
@@ -87,7 +87,8 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
else {
val rawComment = global.expandedDocComment(sym, inTpl.sym).trim
if (rawComment != "") {
- val c = parse(rawComment, global.rawDocComment(sym), global.docCommentPos(sym))
+ val tplOpt = if (currentTpl.isDefined) currentTpl else Some(inTpl)
+ val c = parse(rawComment, global.rawDocComment(sym), global.docCommentPos(sym), tplOpt)
Some(c)
}
else None
@@ -113,7 +114,11 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
constructor0: Option[Body] = None,
source0: Option[String] = None,
inheritDiagram0: List[String] = List.empty,
- contentDiagram0: List[String] = List.empty
+ contentDiagram0: List[String] = List.empty,
+ group0: Option[Body] = None,
+ groupDesc0: Map[String,Body] = Map.empty,
+ groupNames0: Map[String,Body] = Map.empty,
+ groupPrio0: Map[String,Body] = Map.empty
) : Comment = new Comment{
val body = if(body0 isDefined) body0.get else Body(Seq.empty)
val authors = authors0
@@ -132,6 +137,35 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
val source = source0
val inheritDiagram = inheritDiagram0
val contentDiagram = contentDiagram0
+ val groupDesc = groupDesc0
+ val group =
+ group0 match {
+ case Some(Body(List(Paragraph(Chain(List(Summary(Text(groupId)))))))) => Some(groupId.toString.trim)
+ case _ => None
+ }
+ val groupPrio = groupPrio0 flatMap {
+ case (group, body) =>
+ try {
+ body match {
+ case Body(List(Paragraph(Chain(List(Summary(Text(prio))))))) => List(group -> prio.toInt)
+ case _ => List()
+ }
+ } catch {
+ case _: java.lang.NumberFormatException => List()
+ }
+ }
+ val groupNames = groupNames0 flatMap {
+ case (group, body) =>
+ try {
+ body match {
+ case Body(List(Paragraph(Chain(List(Summary(Text(name))))))) if (!name.trim.contains("\n")) => List(group -> (name.trim))
+ case _ => List()
+ }
+ } catch {
+ case _: java.lang.NumberFormatException => List()
+ }
+ }
+
}
protected val endOfText = '\u0003'
@@ -201,7 +235,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
/** A Scaladoc tag linked to a symbol. Returns the name of the tag, the name
* of the symbol, and the rest of the line. */
protected val SymbolTag =
- new Regex("""\s*@(param|tparam|throws)\s+(\S*)\s*(.*)""")
+ new Regex("""\s*@(param|tparam|throws|groupdesc|groupname|groupprio)\s+(\S*)\s*(.*)""")
/** The start of a scaladoc code block */
protected val CodeBlockStart =
@@ -225,7 +259,8 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
* @param comment The expanded comment string (including start and end markers) to be parsed.
* @param src The raw comment source string.
* @param pos The position of the comment in source. */
- protected def parse(comment: String, src: String, pos: Position): Comment = {
+ protected def parse(comment: String, src: String, pos: Position, inTplOpt: Option[DocTemplateImpl] = None): Comment = {
+ assert(!inTplOpt.isDefined || inTplOpt.get != null)
/** The cleaned raw comment as a list of lines. Cleaning removes comment
* start and end markers, line start markers and unnecessary whitespace. */
@@ -348,10 +383,11 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
case None => List.empty
}
- val tagsWithoutDiagram = tags.filterNot(pair => pair._1 == inheritDiagramTag || pair._1 == contentDiagramTag)
+ val stripTags=List(inheritDiagramTag, contentDiagramTag, SimpleTagKey("template"), SimpleTagKey("documentable"))
+ val tagsWithoutDiagram = tags.filterNot(pair => stripTags.contains(pair._1))
val bodyTags: mutable.Map[TagKey, List[Body]] =
- mutable.Map(tagsWithoutDiagram mapValues {tag => tag map (parseWiki(_, pos))} toSeq: _*)
+ mutable.Map(tagsWithoutDiagram mapValues {tag => tag map (parseWiki(_, pos, inTplOpt))} toSeq: _*)
def oneTag(key: SimpleTagKey): Option[Body] =
((bodyTags remove key): @unchecked) match {
@@ -384,7 +420,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
}
val com = createComment (
- body0 = Some(parseWiki(docBody.toString, pos)),
+ body0 = Some(parseWiki(docBody.toString, pos, inTplOpt)),
authors0 = allTags(SimpleTagKey("author")),
see0 = allTags(SimpleTagKey("see")),
result0 = oneTag(SimpleTagKey("return")),
@@ -400,7 +436,11 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
constructor0 = oneTag(SimpleTagKey("constructor")),
source0 = Some(clean(src).mkString("\n")),
inheritDiagram0 = inheritDiagramText,
- contentDiagram0 = contentDiagramText
+ contentDiagram0 = contentDiagramText,
+ group0 = oneTag(SimpleTagKey("group")),
+ groupDesc0 = allSymsOneTag(SimpleTagKey("groupdesc")),
+ groupNames0 = allSymsOneTag(SimpleTagKey("groupname")),
+ groupPrio0 = allSymsOneTag(SimpleTagKey("groupprio"))
)
for ((key, _) <- bodyTags)
@@ -420,8 +460,10 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
* - Removed start-of-line star and one whitespace afterwards (if present).
* - Removed all end-of-line whitespace.
* - Only `endOfLine` is used to mark line endings. */
- def parseWiki(string: String, pos: Position): Body = {
- new WikiParser(string, pos).document()
+ def parseWiki(string: String, pos: Position, inTplOpt: Option[DocTemplateImpl]): Body = {
+ assert(!inTplOpt.isDefined || inTplOpt.get != null)
+
+ new WikiParser(string, pos, inTplOpt).document()
}
/** TODO
@@ -429,7 +471,8 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
* @author Ingo Maier
* @author Manohar Jonnalagedda
* @author Gilles Dubochet */
- protected final class WikiParser(val buffer: String, pos: Position) extends CharReader(buffer) { wiki =>
+ protected final class WikiParser(val buffer: String, pos: Position, inTplOpt: Option[DocTemplateImpl]) extends CharReader(buffer) { wiki =>
+ assert(!inTplOpt.isDefined || inTplOpt.get != null)
var summaryParsed = false
@@ -617,7 +660,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
else if (check(",,")) subscript()
else if (check("[[")) link()
else {
- readUntil { char == safeTagMarker || check("''") || char == '`' || check("__") || char == '^' || check(",,") || check("[[") || isInlineEnd || checkParaEnded || char == endOfLine }
+ readUntil { char == safeTagMarker || check("''") || char == '`' || check("__") || char == '^' || check(",,") || check("[[") || check("{{") || isInlineEnd || checkParaEnded || char == endOfLine }
Text(getRead())
}
}
@@ -719,29 +762,27 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
def link(): Inline = {
val SchemeUri = """([^:]+:.*)""".r
jump("[[")
- readUntil { check("]]") || check(" ") }
+ var parens = 1
+ readUntil { parens += 1; !check("[") }
+ getRead // clear the buffer
+ val start = "[" * parens
+ val stop = "]" * parens
+ //println("link with " + parens + " matching parens")
+ readUntil { check(stop) || check(" ") }
val target = getRead()
val title =
- if (!check("]]")) Some({
+ if (!check(stop)) Some({
jump(" ")
- inline(check("]]"))
+ inline(check(stop))
})
else None
- jump("]]")
+ jump(stop)
(target, title) match {
case (SchemeUri(uri), optTitle) =>
Link(uri, optTitle getOrElse Text(uri))
case (qualName, optTitle) =>
- optTitle foreach (text => reportError(pos, "entity link to " + qualName + " cannot have a custom title'" + text + "'"))
- // XXX rather than warning here we should allow unqualified names
- // to refer to members of the same package. The "package exists"
- // exclusion is because [[scala]] is used in some scaladoc.
- if (!qualName.contains(".") && !definitions.packageExists(qualName))
- reportError(pos, "entity link to " + qualName + " should be a fully qualified name")
-
- // move the template resolution as late as possible
- EntityLink(qualName, () => findTemplate(qualName))
+ makeEntityLink(optTitle getOrElse Text(target), pos, target, inTplOpt)
}
}
diff --git a/src/compiler/scala/tools/nsc/doc/model/diagram/Diagram.scala b/src/compiler/scala/tools/nsc/doc/model/diagram/Diagram.scala
index 8527ca4039..c2aa1f17f3 100644
--- a/src/compiler/scala/tools/nsc/doc/model/diagram/Diagram.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/diagram/Diagram.scala
@@ -13,18 +13,18 @@ import model._
abstract class Diagram {
def nodes: List[Node]
def edges: List[(Node, List[Node])]
- def isPackageDiagram = false
- def isClassDiagram = false
+ def isContentDiagram = false // Implemented by ContentDiagram
+ def isInheritanceDiagram = false // Implemented by InheritanceDiagram
def depthInfo: DepthInfo
}
-case class PackageDiagram(nodes:List[/*Class*/Node], edges:List[(Node, List[Node])]) extends Diagram {
- override def isPackageDiagram = true
- lazy val depthInfo = new PackageDiagramDepth(this)
+case class ContentDiagram(nodes:List[/*Class*/Node], edges:List[(Node, List[Node])]) extends Diagram {
+ override def isContentDiagram = true
+ lazy val depthInfo = new ContentDiagramDepth(this)
}
/** A class diagram */
-case class ClassDiagram(thisNode: ThisNode,
+case class InheritanceDiagram(thisNode: ThisNode,
superClasses: List[/*Class*/Node],
subClasses: List[/*Class*/Node],
incomingImplicits: List[ImplicitNode],
@@ -33,7 +33,7 @@ case class ClassDiagram(thisNode: ThisNode,
def edges = (thisNode -> (superClasses ::: outgoingImplicits)) ::
(subClasses ::: incomingImplicits).map(_ -> List(thisNode))
- override def isClassDiagram = true
+ override def isInheritanceDiagram = true
lazy val depthInfo = new DepthInfo {
def maxDepth = 3
def nodeDepth(node: Node) =
@@ -70,7 +70,8 @@ abstract class Node {
def isClassNode = if (tpl.isDefined) (tpl.get.isClass || tpl.get.qualifiedName == "scala.AnyRef") else false
def isTraitNode = if (tpl.isDefined) tpl.get.isTrait else false
def isObjectNode= if (tpl.isDefined) tpl.get.isObject else false
- def isOtherNode = !(isClassNode || isTraitNode || isObjectNode)
+ def isTypeNode = if (doctpl.isDefined) doctpl.get.isAbstractType || doctpl.get.isAliasType else false
+ def isOtherNode = !(isClassNode || isTraitNode || isObjectNode || isTypeNode)
def isImplicitNode = false
def isOutsideNode = false
def tooltip: Option[String]
@@ -91,6 +92,7 @@ abstract class Node {
object Node { def unapply(n: Node): Option[(TypeEntity, Option[TemplateEntity])] = Some((n.tpe, n.tpl)) }
object ClassNode { def unapply(n: Node): Option[(TypeEntity, Option[TemplateEntity])] = if (n.isClassNode) Some((n.tpe, n.tpl)) else None }
object TraitNode { def unapply(n: Node): Option[(TypeEntity, Option[TemplateEntity])] = if (n.isTraitNode) Some((n.tpe, n.tpl)) else None }
+object TypeNode { def unapply(n: Node): Option[(TypeEntity, Option[TemplateEntity])] = if (n.isTypeNode) Some((n.tpe, n.tpl)) else None }
object ObjectNode { def unapply(n: Node): Option[(TypeEntity, Option[TemplateEntity])] = if (n.isObjectNode) Some((n.tpe, n.tpl)) else None }
object OutsideNode { def unapply(n: Node): Option[(TypeEntity, Option[TemplateEntity])] = if (n.isOutsideNode) Some((n.tpe, n.tpl)) else None }
object OtherNode { def unapply(n: Node): Option[(TypeEntity, Option[TemplateEntity])] = if (n.isOtherNode) Some((n.tpe, n.tpl)) else None }
@@ -98,24 +100,24 @@ object OtherNode { def unapply(n: Node): Option[(TypeEntity, Option[TemplateEn
/** The node for the current class */
-case class ThisNode(tpe: TypeEntity, tpl: Option[TemplateEntity], tooltip: Option[String] = None) extends Node { override def isThisNode = true }
+case class ThisNode(tpe: TypeEntity, tpl: Option[TemplateEntity])(val tooltip: Option[String] = None) extends Node { override def isThisNode = true }
/** The usual node */
-case class NormalNode(tpe: TypeEntity, tpl: Option[TemplateEntity], tooltip: Option[String] = None) extends Node { override def isNormalNode = true }
+case class NormalNode(tpe: TypeEntity, tpl: Option[TemplateEntity])(val tooltip: Option[String] = None) extends Node { override def isNormalNode = true }
/** A class or trait the thisnode can be converted to by an implicit conversion
* TODO: I think it makes more sense to use the tpe links to templates instead of the TemplateEntity for implicit nodes
* since some implicit conversions convert the class to complex types that cannot be represented as a single tmeplate
*/
-case class ImplicitNode(tpe: TypeEntity, tpl: Option[TemplateEntity], tooltip: Option[String] = None) extends Node { override def isImplicitNode = true }
+case class ImplicitNode(tpe: TypeEntity, tpl: Option[TemplateEntity])(val tooltip: Option[String] = None) extends Node { override def isImplicitNode = true }
/** An outside node is shown in packages when a class from a different package makes it to the package diagram due to
* its relation to a class in the template (see @contentDiagram hideInheritedNodes annotation) */
-case class OutsideNode(tpe: TypeEntity, tpl: Option[TemplateEntity], tooltip: Option[String] = None) extends Node { override def isOutsideNode = true }
+case class OutsideNode(tpe: TypeEntity, tpl: Option[TemplateEntity])(val tooltip: Option[String] = None) extends Node { override def isOutsideNode = true }
// Computing and offering node depth information
-class PackageDiagramDepth(pack: PackageDiagram) extends DepthInfo {
+class ContentDiagramDepth(pack: ContentDiagram) extends DepthInfo {
private[this] var _maxDepth = 0
private[this] var _nodeDepth = Map[Node, Int]()
private[this] var seedNodes = Set[Node]()
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 1a8ad193aa..2645d8fd14 100644
--- a/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala
@@ -18,21 +18,14 @@ import scala.collection.immutable.SortedMap
* @author Vlad Ureche
*/
trait DiagramFactory extends DiagramDirectiveParser {
- this: ModelFactory with DiagramFactory with CommentFactory with TreeFactory =>
+ this: ModelFactory with ModelFactoryTypeSupport with DiagramFactory with CommentFactory with TreeFactory =>
import this.global.definitions._
import this.global._
// the following can used for hardcoding different relations into the diagram, for bootstrapping purposes
- lazy val AnyNode = normalNode(AnyClass)
- lazy val AnyRefNode = normalNode(AnyRefClass)
- lazy val AnyValNode = normalNode(AnyValClass)
- lazy val NullNode = normalNode(NullClass)
- lazy val NothingNode = normalNode(NothingClass)
- def normalNode(sym: Symbol) =
- NormalNode(makeTemplate(sym).ownType, Some(makeTemplate(sym)))
def aggregationNode(text: String) =
- NormalNode(new TypeEntity { val name = text; val refEntity = SortedMap[Int, (TemplateEntity, Int)]() }, None)
+ NormalNode(new TypeEntity { val name = text; val refEntity = SortedMap[Int, (LinkTo, Int)]() }, None)()
/** Create the inheritance diagram for this template */
def makeInheritanceDiagram(tpl: DocTemplateImpl): Option[Diagram] = {
@@ -52,31 +45,30 @@ trait DiagramFactory extends DiagramDirectiveParser {
None
else {
// the main node
- val thisNode = ThisNode(tpl.ownType, Some(tpl), Some(tpl.qualifiedName + " (this " + tpl.kind + ")"))
+ val thisNode = ThisNode(tpl.resultType, Some(tpl))(Some(tpl.qualifiedName + " (this " + tpl.kind + ")"))
// superclasses
var superclasses: List[Node] =
tpl.parentTypes.collect {
- case p: (TemplateEntity, TypeEntity) if !classExcluded(p._1) => NormalNode(p._2, Some(p._1))
+ case p: (TemplateEntity, TypeEntity) if !classExcluded(p._1) => NormalNode(p._2, Some(p._1))()
}.reverse
// incoming implcit conversions
lazy val incomingImplicitNodes = tpl.incomingImplicitlyConvertedClasses.map {
case (incomingTpl, conv) =>
- ImplicitNode(incomingTpl.ownType, Some(incomingTpl), implicitTooltip(from=incomingTpl, to=tpl, conv=conv))
+ ImplicitNode(makeType(incomingTpl.sym.tpe, tpl), Some(incomingTpl))(implicitTooltip(from=incomingTpl, to=tpl, conv=conv))
}
// subclasses
var subclasses: List[Node] =
- tpl.directSubClasses.flatMap {
- case d: TemplateEntity if !classExcluded(d) => List(NormalNode(d.ownType, Some(d)))
- case _ => Nil
+ tpl.directSubClasses.collect {
+ case d: TemplateImpl if !classExcluded(d) => NormalNode(makeType(d.sym.tpe, tpl), Some(d))()
}.sortBy(_.tpl.get.name)(implicitly[Ordering[String]].reverse)
// outgoing implicit coversions
lazy val outgoingImplicitNodes = tpl.outgoingImplicitlyConvertedClasses.map {
case (outgoingTpl, outgoingType, conv) =>
- ImplicitNode(outgoingType, Some(outgoingTpl), implicitTooltip(from=tpl, to=tpl, conv=conv))
+ ImplicitNode(outgoingType, Some(outgoingTpl))(implicitTooltip(from=tpl, to=tpl, conv=conv))
}
// TODO: Everyone should be able to use the @{inherit,content}Diagram annotation to change the diagrams.
@@ -93,7 +85,7 @@ trait DiagramFactory extends DiagramDirectiveParser {
val filteredImplicitOutgoingNodes = if (diagramFilter.hideOutgoingImplicits) Nil else outgoingImplicitNodes
// final diagram filter
- filterDiagram(ClassDiagram(thisNode, filteredSuperclasses.reverse, filteredSubclasses.reverse, filteredIncomingImplicits, filteredImplicitOutgoingNodes), diagramFilter)
+ filterDiagram(InheritanceDiagram(thisNode, filteredSuperclasses.reverse, filteredSubclasses.reverse, filteredIncomingImplicits, filteredImplicitOutgoingNodes), diagramFilter)
}
tModel += System.currentTimeMillis
@@ -129,12 +121,10 @@ trait DiagramFactory extends DiagramDirectiveParser {
// for each node, add its subclasses
for (node <- nodesAll if !classExcluded(node)) {
node match {
- case dnode: DocTemplateImpl =>
- var superClasses = dnode.parentTypes.map(_._1)
+ case dnode: MemberTemplateImpl =>
+ var superClasses = dnode.parentTypes.map(_._1).filter(nodesAll.contains(_))
- superClasses = superClasses.filter(nodesAll.contains(_))
-
- // TODO: Everyone should be able to use the @{inherit,content}Diagram annotation to change the diagrams.
+ // TODO: Everyone should be able to use the @{inherit,content}Diagram annotation to add nodes to diagrams.
if (pack.sym == ScalaPackage)
if (dnode.sym == NullClass)
superClasses = List(makeTemplate(AnyRefClass))
@@ -149,7 +139,12 @@ trait DiagramFactory extends DiagramDirectiveParser {
case _ =>
}
- mapNodes += node -> (if (node.inTemplate == pack) NormalNode(node.ownType, Some(node)) else OutsideNode(node.ownType, Some(node)))
+ mapNodes += node -> (
+ if (node.inTemplate == pack && (node.isDocTemplate || node.isAbstractType || node.isAliasType))
+ NormalNode(node.resultType, Some(node))()
+ else
+ OutsideNode(node.resultType, Some(node))()
+ )
}
if (nodesShown.isEmpty)
@@ -173,9 +168,12 @@ trait DiagramFactory extends DiagramDirectiveParser {
val anyRefSubtypes = Nil
val allAnyRefTypes = aggregationNode("All AnyRef subtypes")
val nullTemplate = makeTemplate(NullClass)
- PackageDiagram(allAnyRefTypes::nodes, (mapNodes(nullTemplate), allAnyRefTypes::anyRefSubtypes)::edges.filterNot(_._1.tpl == Some(nullTemplate)))
+ if (nullTemplate.isDocTemplate)
+ ContentDiagram(allAnyRefTypes::nodes, (mapNodes(nullTemplate), allAnyRefTypes::anyRefSubtypes)::edges.filterNot(_._1.tpl == Some(nullTemplate)))
+ else
+ ContentDiagram(nodes, edges)
} else
- PackageDiagram(nodes, edges)
+ ContentDiagram(nodes, edges)
filterDiagram(diagram, diagramFilter)
}
@@ -200,10 +198,10 @@ trait DiagramFactory extends DiagramDirectiveParser {
else {
// Final diagram, with the filtered nodes and edges
diagram match {
- case ClassDiagram(thisNode, _, _, _, _) if diagramFilter.hideNode(thisNode) =>
+ case InheritanceDiagram(thisNode, _, _, _, _) if diagramFilter.hideNode(thisNode) =>
None
- case ClassDiagram(thisNode, superClasses, subClasses, incomingImplicits, outgoingImplicits) =>
+ case InheritanceDiagram(thisNode, superClasses, subClasses, incomingImplicits, outgoingImplicits) =>
def hideIncoming(node: Node): Boolean =
diagramFilter.hideNode(node) || diagramFilter.hideEdge(node, thisNode)
@@ -214,13 +212,13 @@ trait DiagramFactory extends DiagramDirectiveParser {
// println(thisNode)
// println(superClasses.map(cl => "super: " + cl + " " + hideOutgoing(cl)).mkString("\n"))
// println(subClasses.map(cl => "sub: " + cl + " " + hideIncoming(cl)).mkString("\n"))
- Some(ClassDiagram(thisNode,
+ Some(InheritanceDiagram(thisNode,
superClasses.filterNot(hideOutgoing(_)),
subClasses.filterNot(hideIncoming(_)),
incomingImplicits.filterNot(hideIncoming(_)),
outgoingImplicits.filterNot(hideOutgoing(_))))
- case PackageDiagram(nodes0, edges0) =>
+ case ContentDiagram(nodes0, edges0) =>
// Filter out all edges that:
// (1) are sources of hidden classes
// (2) are manually hidden by the user
@@ -242,7 +240,7 @@ trait DiagramFactory extends DiagramDirectiveParser {
val sourceNodes = edges.map(_._1)
val sinkNodes = edges.map(_._2).flatten
val nodes = (sourceNodes ::: sinkNodes).distinct
- Some(PackageDiagram(nodes, edges))
+ Some(ContentDiagram(nodes, edges))
}
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 9371af4848..7dff055172 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -1972,7 +1972,8 @@ trait Typers extends Modes with Adaptations with Tags {
case SilentResultValue(tpt) =>
val alias = enclClass.newAliasType(name.toTypeName, useCase.pos)
val tparams = cloneSymbolsAtOwner(tpt.tpe.typeSymbol.typeParams, alias)
- alias setInfo typeFun(tparams, appliedType(tpt.tpe, tparams map (_.tpe)))
+ val newInfo = genPolyType(tparams, appliedType(tpt.tpe, tparams map (_.tpe)))
+ alias setInfo newInfo
context.scope.enter(alias)
case _ =>
}
diff --git a/src/compiler/scala/tools/nsc/util/DocStrings.scala b/src/compiler/scala/tools/nsc/util/DocStrings.scala
index f4ce6d6ef1..c88414c423 100755
--- a/src/compiler/scala/tools/nsc/util/DocStrings.scala
+++ b/src/compiler/scala/tools/nsc/util/DocStrings.scala
@@ -144,6 +144,12 @@ object DocStrings {
}
/** Optionally start and end index of return section in `str`, or `None`
+ * if `str` does not have a @group. */
+ def groupDoc(str: String, sections: List[(Int, Int)]): Option[(Int, Int)] =
+ sections find (startsWithTag(str, _, "@group"))
+
+
+ /** Optionally start and end index of return section in `str`, or `None`
* if `str` does not have a @return.
*/
def returnDoc(str: String, sections: List[(Int, Int)]): Option[(Int, Int)] =
diff --git a/src/library-aux/scala/AnyRef.scala b/src/library-aux/scala/AnyRef.scala
index 1eefb0c806..7d8b9f9e76 100644
--- a/src/library-aux/scala/AnyRef.scala
+++ b/src/library-aux/scala/AnyRef.scala
@@ -10,6 +10,7 @@ package scala
/** Class `AnyRef` is the root class of all ''reference types''.
* All types except the value types descend from this class.
+ * @template
*/
trait AnyRef extends Any {
diff --git a/src/library/scala/collection/immutable/List.scala b/src/library/scala/collection/immutable/List.scala
index 6fd8d143ee..74dc385f99 100644
--- a/src/library/scala/collection/immutable/List.scala
+++ b/src/library/scala/collection/immutable/List.scala
@@ -62,6 +62,7 @@ import java.io._
* section on `Lists` for more information.
*
* @define coll list
+ * @define Coll `List`
* @define thatinfo the class of the returned collection. In the standard library configuration,
* `That` is always `List[B]` because an implicit of type `CanBuildFrom[List, B, That]`
* is defined in object `List`.
@@ -96,7 +97,7 @@ sealed abstract class List[+A] extends AbstractSeq[A]
*
* @usecase def ::(x: A): List[A]
* @inheritdoc
- *
+ *
* Example:
* {{{1 :: List(2, 3) = List(2, 3).::(1) = List(1, 2, 3)}}}
*/
diff --git a/src/library/scala/collection/immutable/StringOps.scala b/src/library/scala/collection/immutable/StringOps.scala
index 633821ecea..7e60cc7195 100644
--- a/src/library/scala/collection/immutable/StringOps.scala
+++ b/src/library/scala/collection/immutable/StringOps.scala
@@ -25,7 +25,7 @@ import mutable.StringBuilder
* @param repr the actual representation of this string operations object.
*
* @since 2.8
- * @define Coll `StringOps`
+ * @define Coll `String`
* @define coll string
*/
final class StringOps(override val repr: String) extends AnyVal with StringLike[String] {
diff --git a/src/library/scala/collection/mutable/ArrayOps.scala b/src/library/scala/collection/mutable/ArrayOps.scala
index 7a595f211d..21c2aaaec7 100644
--- a/src/library/scala/collection/mutable/ArrayOps.scala
+++ b/src/library/scala/collection/mutable/ArrayOps.scala
@@ -30,7 +30,7 @@ import parallel.mutable.ParArray
*
* @tparam T type of the elements contained in this array.
*
- * @define Coll `ArrayOps`
+ * @define Coll `Array`
* @define orderDependent
* @define orderDependentFold
* @define mayNotTerminateInf
diff --git a/src/partest/scala/tools/partest/ScaladocModelTest.scala b/src/partest/scala/tools/partest/ScaladocModelTest.scala
index de5354d4a0..ffc5e74cc0 100644
--- a/src/partest/scala/tools/partest/ScaladocModelTest.scala
+++ b/src/partest/scala/tools/partest/ScaladocModelTest.scala
@@ -11,6 +11,8 @@ import scala.tools.nsc._
import scala.tools.nsc.util.CommandLineParser
import scala.tools.nsc.doc.{Settings, DocFactory, Universe}
import scala.tools.nsc.doc.model._
+import scala.tools.nsc.doc.model.diagram._
+import scala.tools.nsc.doc.model.comment._
import scala.tools.nsc.reporters.ConsoleReporter
/** A class for testing scaladoc model generation
@@ -102,13 +104,22 @@ abstract class ScaladocModelTest extends DirectTest {
class TemplateAccess(tpl: DocTemplateEntity) {
def _class(name: String): DocTemplateEntity = getTheFirst(_classes(name), tpl.qualifiedName + ".class(" + name + ")")
- def _classes(name: String): List[DocTemplateEntity] = tpl.templates.filter(_.name == name).collect({ case c: Class => c})
+ def _classes(name: String): List[DocTemplateEntity] = tpl.templates.filter(_.name == name).collect({ case c: DocTemplateEntity with Class => c})
+
+ def _classMbr(name: String): MemberTemplateEntity = getTheFirst(_classesMbr(name), tpl.qualifiedName + ".classMember(" + name + ")")
+ def _classesMbr(name: String): List[MemberTemplateEntity] = tpl.templates.filter(_.name == name).collect({ case c: MemberTemplateEntity if c.isClass => c})
def _trait(name: String): DocTemplateEntity = getTheFirst(_traits(name), tpl.qualifiedName + ".trait(" + name + ")")
- def _traits(name: String): List[DocTemplateEntity] = tpl.templates.filter(_.name == name).collect({ case t: Trait => t})
+ def _traits(name: String): List[DocTemplateEntity] = tpl.templates.filter(_.name == name).collect({ case t: DocTemplateEntity with Trait => t})
+
+ def _traitMbr(name: String): MemberTemplateEntity = getTheFirst(_traitsMbr(name), tpl.qualifiedName + ".traitMember(" + name + ")")
+ def _traitsMbr(name: String): List[MemberTemplateEntity] = tpl.templates.filter(_.name == name).collect({ case t: MemberTemplateEntity if t.isTrait => t})
def _object(name: String): DocTemplateEntity = getTheFirst(_objects(name), tpl.qualifiedName + ".object(" + name + ")")
- def _objects(name: String): List[DocTemplateEntity] = tpl.templates.filter(_.name == name).collect({ case o: Object => o})
+ def _objects(name: String): List[DocTemplateEntity] = tpl.templates.filter(_.name == name).collect({ case o: DocTemplateEntity with Object => o})
+
+ def _objectMbr(name: String): MemberTemplateEntity = getTheFirst(_objectsMbr(name), tpl.qualifiedName + ".objectMember(" + name + ")")
+ def _objectsMbr(name: String): List[MemberTemplateEntity] = tpl.templates.filter(_.name == name).collect({ case o: MemberTemplateEntity if o.isObject => o})
def _method(name: String): Def = getTheFirst(_methods(name), tpl.qualifiedName + ".method(" + name + ")")
def _methods(name: String): List[Def] = tpl.methods.filter(_.name == name)
@@ -118,6 +129,18 @@ abstract class ScaladocModelTest extends DirectTest {
def _conversion(name: String): ImplicitConversion = getTheFirst(_conversions(name), tpl.qualifiedName + ".conversion(" + name + ")")
def _conversions(name: String): List[ImplicitConversion] = tpl.conversions.filter(_.conversionQualifiedName == name)
+
+ def _absType(name: String): MemberEntity = getTheFirst(_absTypes(name), tpl.qualifiedName + ".abstractType(" + name + ")")
+ def _absTypes(name: String): List[MemberEntity] = tpl.members.filter(mbr => mbr.name == name && mbr.isAbstractType)
+
+ def _absTypeTpl(name: String): DocTemplateEntity = getTheFirst(_absTypeTpls(name), tpl.qualifiedName + ".abstractType(" + name + ")")
+ def _absTypeTpls(name: String): List[DocTemplateEntity] = tpl.members.collect({ case dtpl: DocTemplateEntity with AbstractType if dtpl.name == name => dtpl })
+
+ def _aliasType(name: String): MemberEntity = getTheFirst(_aliasTypes(name), tpl.qualifiedName + ".aliasType(" + name + ")")
+ def _aliasTypes(name: String): List[MemberEntity] = tpl.members.filter(mbr => mbr.name == name && mbr.isAliasType)
+
+ def _aliasTypeTpl(name: String): DocTemplateEntity = getTheFirst(_aliasTypeTpls(name), tpl.qualifiedName + ".aliasType(" + name + ")")
+ def _aliasTypeTpls(name: String): List[DocTemplateEntity] = tpl.members.collect({ case dtpl: DocTemplateEntity with AliasType if dtpl.name == name => dtpl })
}
class PackageAccess(pack: Package) extends TemplateAccess(pack) {
@@ -140,7 +163,43 @@ abstract class ScaladocModelTest extends DirectTest {
case 1 => list.head
case 0 => sys.error("Error getting " + expl + ": No such element.")
case _ => sys.error("Error getting " + expl + ": " + list.length + " elements with this name. " +
- "All elements in list: [" + list.mkString(", ") + "]")
+ "All elements in list: [" + list.map({
+ case ent: Entity => ent.kind + " " + ent.qualifiedName
+ case other => other.toString
+ }).mkString(", ") + "]")
+ }
+
+ def extractCommentText(c: Any) = {
+ def extractText(body: Any): String = body match {
+ case s: String => s
+ case s: Seq[_] => s.toList.map(extractText(_)).mkString
+ case p: Product => p.productIterator.toList.map(extractText(_)).mkString
+ case _ => ""
+ }
+ c match {
+ case c: Comment =>
+ extractText(c.body)
+ case b: Body =>
+ extractText(b)
+ }
+ }
+
+ def countLinks(c: Comment, p: EntityLink => Boolean) = {
+ def countLinks(body: Any): Int = body match {
+ case el: EntityLink if p(el) => 1
+ case s: Seq[_] => s.toList.map(countLinks(_)).sum
+ case p: Product => p.productIterator.toList.map(countLinks(_)).sum
+ case _ => 0
+ }
+ countLinks(c.body)
+ }
+
+ def testDiagram(doc: DocTemplateEntity, diag: Option[Diagram], nodes: Int, edges: Int) = {
+ assert(diag.isDefined, doc.qualifiedName + " diagram missing")
+ assert(diag.get.nodes.length == nodes,
+ doc.qualifiedName + "'s diagram: node count " + diag.get.nodes.length + " == " + nodes)
+ assert(diag.get.edges.map(_._2.length).sum == edges,
+ doc.qualifiedName + "'s diagram: edge count " + diag.get.edges.length + " == " + edges)
}
}
}
diff --git a/test/scaladoc/resources/SI-3314-diagrams.scala b/test/scaladoc/resources/SI-3314-diagrams.scala
new file mode 100644
index 0000000000..b80a97b522
--- /dev/null
+++ b/test/scaladoc/resources/SI-3314-diagrams.scala
@@ -0,0 +1,78 @@
+package scala.test.scaladoc {
+
+ /** Check the interaction between SI-3314 and diagrams
+ * - the three enumerations below should get valid content diagrams:
+ * Value
+ * __________/|\__________
+ * / / / | \ \ \
+ * Mon Tue Wed Thu Fri Sat Sun
+ *
+ * - each member should receive an inhertiance diagram:
+ * Value
+ * |
+ * |
+ * {Mon,Tue,Wed,Thu,Fri,Sat,Sun}
+ */
+ package diagrams {
+
+ /** @contentDiagram
+ * @inheritanceDiagram hideDiagram */
+ trait WeekDayTraitWithDiagram extends Enumeration {
+ type WeekDay = Value
+ /** @inheritanceDiagram */
+ object Mon extends WeekDay
+ /** @inheritanceDiagram */
+ object Tue extends WeekDay
+ /** @inheritanceDiagram */
+ object Wed extends WeekDay
+ /** @inheritanceDiagram */
+ object Thu extends WeekDay
+ /** @inheritanceDiagram */
+ object Fri extends WeekDay
+ /** @inheritanceDiagram */
+ object Sat extends WeekDay
+ /** @inheritanceDiagram */
+ object Sun extends WeekDay
+ }
+
+ /** @contentDiagram
+ * @inheritanceDiagram hideDiagram */
+ class WeekDayClassWithDiagram extends Enumeration {
+ type WeekDay = Value
+ /** @inheritanceDiagram */
+ object Mon extends WeekDay
+ /** @inheritanceDiagram */
+ object Tue extends WeekDay
+ /** @inheritanceDiagram */
+ object Wed extends WeekDay
+ /** @inheritanceDiagram */
+ object Thu extends WeekDay
+ /** @inheritanceDiagram */
+ object Fri extends WeekDay
+ /** @inheritanceDiagram */
+ object Sat extends WeekDay
+ /** @inheritanceDiagram */
+ object Sun extends WeekDay
+ }
+
+ /** @contentDiagram
+ * @inheritanceDiagram hideDiagram */
+ object WeekDayObjectWithDiagram extends Enumeration {
+ type WeekDay = Value
+ /** @inheritanceDiagram */
+ object Mon extends WeekDay
+ /** @inheritanceDiagram */
+ object Tue extends WeekDay
+ /** @inheritanceDiagram */
+ object Wed extends WeekDay
+ /** @inheritanceDiagram */
+ object Thu extends WeekDay
+ /** @inheritanceDiagram */
+ object Fri extends WeekDay
+ /** @inheritanceDiagram */
+ object Sat extends WeekDay
+ /** @inheritanceDiagram */
+ object Sun extends WeekDay
+ }
+ }
+} \ No newline at end of file
diff --git a/test/scaladoc/resources/SI-3314.scala b/test/scaladoc/resources/SI-3314.scala
new file mode 100644
index 0000000000..9e0afdce9d
--- /dev/null
+++ b/test/scaladoc/resources/SI-3314.scala
@@ -0,0 +1,85 @@
+package scala.test.scaladoc {
+
+ // testing inherited <documented> templates (Enum.Value is included in the source, thus is documented in scaladoc)
+ package test1 {
+ class Enum {
+ abstract class Value
+ class Val extends Value
+ def Value(): Value = new Val
+ }
+
+ object Constants extends Enum {
+ def a = Value
+ }
+ }
+
+ // testing inherited <not documented> templates (scala.Enumeration.Value is taken from the library, thus is not
+ // documented in the scaladoc pages -- but should be inherited to make things clear!)
+ package test2 {
+ trait WeekDayTrait extends Enumeration {
+ type WeekDay = Value
+ val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value
+ }
+
+ class WeekDayClass extends Enumeration {
+ type WeekDay = Value
+ val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value
+ }
+
+ object WeekDayObject extends Enumeration {
+ type WeekDay = Value
+ val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value
+ }
+
+ object UserObject {
+ def isWorkingDay1(d: scala.test.scaladoc.test2.WeekDayClass#Value) = false
+ def isWorkingDay2(d: scala.test.scaladoc.test2.WeekDayClass#WeekDay) = false
+ def isWorkingDay3(d: scala.test.scaladoc.test2.WeekDayTrait#Value) = false
+ def isWorkingDay4(d: scala.test.scaladoc.test2.WeekDayTrait#WeekDay) = false
+ def isWorkingDay5(d: scala.test.scaladoc.test2.WeekDayObject.Value) = false
+ def isWorkingDay6(d: scala.test.scaladoc.test2.WeekDayObject.WeekDay) = false
+ import WeekDayObject._
+ def isWorkingDay7(d: Value) = ! (d == Sat || d == Sun)
+ def isWorkingDay8(d: WeekDay) = ! (d == Sat || d == Sun)
+ def isWorkingDay9(d: WeekDayObject.Value) = ! (d == Sat || d == Sun)
+ }
+
+ class UserClass {
+ def isWorkingDay1(d: scala.test.scaladoc.test2.WeekDayClass#Value) = false
+ def isWorkingDay2(d: scala.test.scaladoc.test2.WeekDayClass#WeekDay) = false
+ def isWorkingDay3(d: scala.test.scaladoc.test2.WeekDayTrait#Value) = false
+ def isWorkingDay4(d: scala.test.scaladoc.test2.WeekDayTrait#WeekDay) = false
+ def isWorkingDay5(d: scala.test.scaladoc.test2.WeekDayObject.Value) = false
+ def isWorkingDay6(d: scala.test.scaladoc.test2.WeekDayObject.WeekDay) = false
+ import WeekDayObject._
+ def isWorkingDay7(d: Value) = ! (d == Sat || d == Sun)
+ def isWorkingDay8(d: WeekDay) = ! (d == Sat || d == Sun)
+ def isWorkingDay9(d: WeekDayObject.Value) = ! (d == Sat || d == Sun)
+ }
+
+ trait UserTrait {
+ def isWorkingDay1(d: scala.test.scaladoc.test2.WeekDayClass#Value) = false
+ def isWorkingDay2(d: scala.test.scaladoc.test2.WeekDayClass#WeekDay) = false
+ def isWorkingDay3(d: scala.test.scaladoc.test2.WeekDayTrait#Value) = false
+ def isWorkingDay4(d: scala.test.scaladoc.test2.WeekDayTrait#WeekDay) = false
+ def isWorkingDay5(d: scala.test.scaladoc.test2.WeekDayObject.Value) = false
+ def isWorkingDay6(d: scala.test.scaladoc.test2.WeekDayObject.WeekDay) = false
+ import WeekDayObject._
+ def isWorkingDay7(d: Value) = ! (d == Sat || d == Sun)
+ def isWorkingDay8(d: WeekDay) = ! (d == Sat || d == Sun)
+ def isWorkingDay9(d: WeekDayObject.Value) = ! (d == Sat || d == Sun)
+ }
+ }
+
+ // testing type lambdas and useless prefixes (should be referenced as T instead of foo.T in the first example)
+ package test3 {
+ import language.higherKinds
+ object `package` {
+ trait T
+ trait A
+ trait X
+ def foo[T](x: T) = 7
+ def bar[A](x: ({type Lambda[X] <: Either[A, X]})#Lambda[String]) = 5
+ }
+ }
+}
diff --git a/test/scaladoc/resources/SI-4360.scala b/test/scaladoc/resources/SI-4360.scala
new file mode 100644
index 0000000000..8e8b96afd5
--- /dev/null
+++ b/test/scaladoc/resources/SI-4360.scala
@@ -0,0 +1,42 @@
+package scala.test.scaladoc.prefix {
+ package pack1 {
+
+ class A {
+ class Z
+ }
+
+ class B extends A
+
+ package a {
+ class C
+ }
+
+ package b {
+ class C
+ }
+
+ package c {
+ class C
+
+ class L extends pack2.Z
+
+ class TEST {
+ // test inherited classes
+ def fooCA(x: pack1.A#Z) = 1
+ def fooCB(x: pack1.B#Z) = 1
+ def fooCS(x: pack2.Z#Z) = 1
+ def fooCL(x: L#Z) = 1
+ // test in packages
+ def fooPA(x: pack1.a.C) = 1
+ def fooPB(x: pack1.b.C) = 1
+ def fooPC(x: pack1.c.C) = 1
+ }
+
+ class A extends pack1.A
+ }
+ }
+
+ package pack2 {
+ class Z extends pack1.A
+ }
+} \ No newline at end of file
diff --git a/test/scaladoc/resources/SI-5558.scala b/test/scaladoc/resources/SI-5558.scala
new file mode 100644
index 0000000000..6523438b86
--- /dev/null
+++ b/test/scaladoc/resources/SI-5558.scala
@@ -0,0 +1,6 @@
+package test {
+ class T
+ object `package` {
+ def foo = ???
+ }
+}
diff --git a/test/scaladoc/resources/SI-5784.scala b/test/scaladoc/resources/SI-5784.scala
new file mode 100644
index 0000000000..3731d4998c
--- /dev/null
+++ b/test/scaladoc/resources/SI-5784.scala
@@ -0,0 +1,28 @@
+package test.templates {
+ object `package` {
+ /** @template */
+ type String = java.lang.String
+ val String = new StringCompanion
+ class StringCompanion { def boo = ??? }
+ }
+
+ /** @contentDiagram */
+ trait Base {
+ /** @documentable */
+ type String = test.templates.String
+ /** @template
+ * @inheritanceDiagram */
+ type T <: Foo
+ val T: FooExtractor
+ trait Foo { def foo: Int }
+ trait FooExtractor { def apply(foo: Int); def unapply(t: Foo): Option[Int] }
+ }
+
+ /** @contentDiagram */
+ trait Api extends Base {
+ /** @documentable
+ * @inheritanceDiagram */
+ override type T <: FooApi
+ trait FooApi extends Foo { def bar: String }
+ }
+}
diff --git a/test/scaladoc/resources/SI_4676.scala b/test/scaladoc/resources/SI_4676.scala
deleted file mode 100644
index 00c0fc7ea9..0000000000
--- a/test/scaladoc/resources/SI_4676.scala
+++ /dev/null
@@ -1,4 +0,0 @@
-class SI_4676 {
- type SS = (String,String)
- def x(ss: SS): Int = 3
-}
diff --git a/test/scaladoc/resources/Trac3484.scala b/test/scaladoc/resources/Trac3484.scala
deleted file mode 100644
index 9656ec268d..0000000000
--- a/test/scaladoc/resources/Trac3484.scala
+++ /dev/null
@@ -1,27 +0,0 @@
-class cbf[A, B, C]
-
-/**
- * @define Coll Traversable
- * @define bfreturn $Coll
- */
-class Collection[A] {
- /** What map does...
- *
- * $bfreturn
- * @usecase def map[B](f: A => B): $bfreturn[B]
- *
- */
- def map[B, That](f: A => B)(implicit fact: cbf[Collection[A], B, That]) =
- null
-}
-
-/**
- * @define b John
- * @define a Mister $b
- */
-class SR704 {
- /**
- * Hello $a.
- */
- def foo = 123
-}
diff --git a/test/scaladoc/resources/doc-root/AnyRef.scala b/test/scaladoc/resources/doc-root/AnyRef.scala
index 1eefb0c806..7d8b9f9e76 100644
--- a/test/scaladoc/resources/doc-root/AnyRef.scala
+++ b/test/scaladoc/resources/doc-root/AnyRef.scala
@@ -10,6 +10,7 @@ package scala
/** Class `AnyRef` is the root class of all ''reference types''.
* All types except the value types descend from this class.
+ * @template
*/
trait AnyRef extends Any {
diff --git a/test/scaladoc/resources/implicits-known-type-classes-res.scala b/test/scaladoc/resources/implicits-known-type-classes-res.scala
new file mode 100644
index 0000000000..9ad652947d
--- /dev/null
+++ b/test/scaladoc/resources/implicits-known-type-classes-res.scala
@@ -0,0 +1,38 @@
+/** Tests the "known type classes" feature of scaladoc implicits
+ * if the test fails, please update the correct qualified name of
+ * the type class in src/compiler/scala/tools/nsc/doc/Settings.scala
+ * in the knownTypeClasses map. Thank you! */
+package scala.test.scaladoc.implicits.typeclasses {
+ class A[T]
+ object A {
+ import language.implicitConversions
+ import scala.reflect.{ClassTag, TypeTag}
+ implicit def convertNumeric [T: Numeric] (a: A[T]) = new B(implicitly[Numeric[T]])
+ implicit def convertIntegral [T: Integral] (a: A[T]) = new B(implicitly[Integral[T]])
+ implicit def convertFractional [T: Fractional] (a: A[T]) = new B(implicitly[Fractional[T]])
+ implicit def convertManifest [T: Manifest] (a: A[T]) = new B(implicitly[Manifest[T]])
+ implicit def convertClassManifest [T: ClassManifest] (a: A[T]) = new B(implicitly[ClassManifest[T]])
+ implicit def convertOptManifest [T: OptManifest] (a: A[T]) = new B(implicitly[OptManifest[T]])
+ implicit def convertClassTag [T: ClassTag] (a: A[T]) = new B(implicitly[ClassTag[T]])
+ implicit def convertTypeTag [T: TypeTag] (a: A[T]) = new B(implicitly[TypeTag[T]])
+ type K[X] = Numeric[X]
+ type L[X] = Integral[X]
+ type M[X] = Fractional[X]
+ type N[X] = Manifest[X]
+ type O[X] = ClassManifest[X]
+ type P[X] = OptManifest[X]
+ type Q[X] = ClassTag[X]
+ type R[X] = TypeTag[X]
+ implicit def convertK [T: K] (a: A[T]) = new B(implicitly[K[T]])
+ implicit def convertL [T: L] (a: A[T]) = new B(implicitly[L[T]])
+ implicit def convertM [T: M] (a: A[T]) = new B(implicitly[M[T]])
+ implicit def convertN [T: N] (a: A[T]) = new B(implicitly[N[T]])
+ implicit def convertO [T: O] (a: A[T]) = new B(implicitly[O[T]])
+ implicit def convertP [T: P] (a: A[T]) = new B(implicitly[P[T]])
+ implicit def convertQ [T: Q] (a: A[T]) = new B(implicitly[Q[T]])
+ implicit def convertR [T: R] (a: A[T]) = new B(implicitly[R[T]])
+ }
+ class B[T](t: T) {
+ def typeClass: T = t
+ }
+} \ No newline at end of file
diff --git a/test/scaladoc/resources/implicits-scopes-res.scala b/test/scaladoc/resources/implicits-scopes-res.scala
index aaeb43f95b..c675a645bd 100644
--- a/test/scaladoc/resources/implicits-scopes-res.scala
+++ b/test/scaladoc/resources/implicits-scopes-res.scala
@@ -22,7 +22,7 @@ package test2 {
package classes {
class A
class B { def b = "" }
- object test { /* (new A).b won't compile */ }
+ object test { (new A).b }
}
}
diff --git a/test/scaladoc/resources/links.scala b/test/scaladoc/resources/links.scala
new file mode 100644
index 0000000000..679d0b0dce
--- /dev/null
+++ b/test/scaladoc/resources/links.scala
@@ -0,0 +1,57 @@
+// that would be:
+// SI-5079 "Scaladoc can't link to an object (only a class or trait)"
+// SI-4497 "Links in ScalaDoc - Spec and implementation unsufficient"
+// SI-4224 "Wiki-links should support method targets"
+// SI-3695 "support non-fully-qualified type links in scaladoc comments"
+package scala.test.scaladoc.links {
+ import language.higherKinds
+ class C
+
+ trait Target {
+ type T
+ type S = String
+ class C
+ def foo(i: Int) = 2
+ def foo(s: String) = 3
+ def foo[A[_]](x: A[String]) = 5
+ def foo[A[_[_]]](x: A[List]) = 6
+ val bar: Boolean
+ def baz(c: scala.test.scaladoc.links.C) = 7
+ }
+
+ object Target {
+ type T = Int => Int
+ type S = Int
+ class C
+ def foo(i: Int) = 2
+ def foo(z: String) = 3
+ def foo[A[_]](x: A[String]) = 5
+ def foo[A[_[_]]](x: A[List]) = 6
+ val bar: Boolean = false
+ val onlyInObject = 1
+ def baz(c: scala.test.scaladoc.links.C) = 7
+ }
+
+ /**
+ * 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)
+ */
+ object TEST {
+ def localMethod = 3
+ }
+}
diff --git a/test/scaladoc/resources/package-object-res.scala b/test/scaladoc/resources/package-object-res.scala
index 17d5c0a499..f1f714dd1f 100644
--- a/test/scaladoc/resources/package-object-res.scala
+++ b/test/scaladoc/resources/package-object-res.scala
@@ -1,4 +1,4 @@
-/** This package have A and B.
+/** This package has A and B.
*/
package test {
trait A { def hi = "hello" }
diff --git a/test/scaladoc/run/SI-3314-diagrams.check b/test/scaladoc/run/SI-3314-diagrams.check
new file mode 100644
index 0000000000..619c56180b
--- /dev/null
+++ b/test/scaladoc/run/SI-3314-diagrams.check
@@ -0,0 +1 @@
+Done.
diff --git a/test/scaladoc/run/SI-3314-diagrams.scala b/test/scaladoc/run/SI-3314-diagrams.scala
new file mode 100644
index 0000000000..050c6e7f48
--- /dev/null
+++ b/test/scaladoc/run/SI-3314-diagrams.scala
@@ -0,0 +1,28 @@
+import scala.tools.nsc.doc.model._
+import scala.tools.partest.ScaladocModelTest
+
+object Test extends ScaladocModelTest {
+
+ override def resourceFile = "SI-3314-diagrams.scala"
+
+ // no need for special settings
+ def scaladocSettings = "-diagrams"
+
+ def testModel(rootPackage: Package) = {
+ // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s))
+ import access._
+
+ // just need to check the member exists, access methods will throw an error if there's a problem
+ val base = rootPackage._package("scala")._package("test")._package("scaladoc")
+
+ val diagrams = base._package("diagrams")
+ val templates = List(diagrams._trait("WeekDayTraitWithDiagram"), diagrams._class("WeekDayClassWithDiagram"), diagrams._object("WeekDayObjectWithDiagram"))
+
+ for (template <- templates) {
+ testDiagram(template, template.contentDiagram, 8, 7)
+ val subtemplates = List("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun").map(template._object(_))
+ for (subtemplate <- subtemplates)
+ testDiagram(subtemplate, subtemplate.inheritanceDiagram, 2, 1)
+ }
+ }
+} \ No newline at end of file
diff --git a/test/scaladoc/run/SI-3314.check b/test/scaladoc/run/SI-3314.check
new file mode 100644
index 0000000000..619c56180b
--- /dev/null
+++ b/test/scaladoc/run/SI-3314.check
@@ -0,0 +1 @@
+Done.
diff --git a/test/scaladoc/run/SI-3314.scala b/test/scaladoc/run/SI-3314.scala
new file mode 100644
index 0000000000..fe220b08af
--- /dev/null
+++ b/test/scaladoc/run/SI-3314.scala
@@ -0,0 +1,91 @@
+import scala.tools.nsc.doc.model._
+import scala.tools.partest.ScaladocModelTest
+
+object Test extends ScaladocModelTest {
+
+ override def resourceFile = "SI-3314.scala"
+
+ // no need for special settings
+ def scaladocSettings = "-feature"
+
+ def testModel(rootPackage: Package) = {
+ // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s))
+ import access._
+
+ // just need to check the member exists, access methods will throw an error if there's a problem
+ val base = rootPackage._package("scala")._package("test")._package("scaladoc")
+
+
+
+ // test1
+
+ val test1 = base._package("test1")
+ val test1Value = test1._class("Enum")._method("Value").resultType
+ assert(test1Value.name == "Value", test1Value.name + " == Value")
+ assert(test1Value.refEntity.size == 1, test1Value.refEntity.size + " == 1")
+
+ val test1Constants = test1._object("Constants")._method("a").resultType
+ assert(test1Constants.name == "Value", test1Constants.name + " == Value")
+ assert(test1Constants.refEntity.size == 1, test1Constants.refEntity.size + " == 1")
+ assert(test1Constants.refEntity(0)._1 == LinkToMember(test1._object("Constants")._class("Value"), test1._object("Constants")),
+ test1Constants.refEntity(0)._1 + " == LinkToMember(test1.Enum.Value)")
+
+
+ // test2
+
+ val test2 = base._package("test2")
+ def testDefinition(doc: DocTemplateEntity) = {
+ for (day <- List("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun")) {
+ assert(doc._value(day).resultType.name == "Value",
+ doc._value(day).resultType.name + " == Value")
+ assert(doc._value(day).resultType.refEntity.size == 1,
+ doc._value(day).resultType.refEntity.size + " == 1")
+ assert(doc._value(day).resultType.refEntity(0)._1 == LinkToMember(doc._classMbr("Value"), doc),
+ doc._value(day).resultType.refEntity(0)._1 + " == LinkToMember(" + doc.qualifiedName + ".Value)")
+ }
+ }
+ testDefinition(test2._trait("WeekDayTrait"))
+ testDefinition(test2._class("WeekDayClass"))
+ testDefinition(test2._object("WeekDayObject"))
+
+ def testUsage(doc: DocTemplateEntity) = {
+ val ValueInClass = test2._class("WeekDayClass")._classMbr("Value")
+ val ValueInTrait = test2._trait("WeekDayTrait")._classMbr("Value")
+ val ValueInObject = test2._object("WeekDayObject")._classMbr("Value")
+ val WeekDayInObject = test2._object("WeekDayObject")._member("WeekDay")
+
+ val expected = List(
+ ("isWorkingDay1", "WeekDayClass.Value", ValueInClass),
+ ("isWorkingDay2", "WeekDayClass.Value", ValueInClass),
+ ("isWorkingDay3", "WeekDayTrait.Value", ValueInTrait),
+ ("isWorkingDay4", "WeekDayTrait.Value", ValueInTrait),
+ ("isWorkingDay5", "WeekDayObject.Value", ValueInObject),
+ ("isWorkingDay6", "WeekDay", WeekDayInObject),
+ ("isWorkingDay7", "WeekDayObject.Value", ValueInObject),
+ ("isWorkingDay8", "WeekDay", WeekDayInObject),
+ ("isWorkingDay9", "WeekDayObject.Value", ValueInObject))
+
+ for ((method, name, ref) <- expected) {
+ assert(doc._method(method).valueParams(0)(0).resultType.name == name,
+ doc._method(method).valueParams(0)(0).resultType.name + " == " + name + " (in " + doc + "." + method + ")")
+ assert(doc._method(method).valueParams(0)(0).resultType.refEntity.size == 1,
+ doc._method(method).valueParams(0)(0).resultType.refEntity.size + " == " + 1 + " (in " + doc + "." + method + ")")
+ assert(doc._method(method).valueParams(0)(0).resultType.refEntity(0)._1 == LinkToMember(ref, ref.inTemplate),
+ doc._method(method).valueParams(0)(0).resultType.refEntity(0)._1 + " == LinkToMember(" + ref.qualifiedName + ") (in " + doc + "." + method + ")")
+ }
+ }
+ testUsage(test2._object("UserObject"))
+ testUsage(test2._class("UserClass"))
+ testUsage(test2._trait("UserTrait"))
+
+
+ // test3
+ val test3 = base._package("test3")
+ val foo = test3._method("foo")
+ assert(foo.valueParams(0)(0).resultType.name == "T",
+ foo.valueParams(0)(0).resultType.name + " == T")
+ val bar = test3._method("bar")
+ assert(bar.valueParams(0)(0).resultType.name == "(AnyRef { type Lambda[X] <: Either[A,X] })#Lambda[String]",
+ bar.valueParams(0)(0).resultType.name + " == (AnyRef { type Lambda[X] <: Either[A,X] })#Lambda[String]")
+ }
+} \ No newline at end of file
diff --git a/test/scaladoc/run/SI-3448.check b/test/scaladoc/run/SI-3448.check
new file mode 100644
index 0000000000..619c56180b
--- /dev/null
+++ b/test/scaladoc/run/SI-3448.check
@@ -0,0 +1 @@
+Done.
diff --git a/test/scaladoc/run/SI-3448.scala b/test/scaladoc/run/SI-3448.scala
new file mode 100644
index 0000000000..a2d3f59596
--- /dev/null
+++ b/test/scaladoc/run/SI-3448.scala
@@ -0,0 +1,38 @@
+import scala.tools.nsc.doc.model._
+import scala.tools.partest.ScaladocModelTest
+
+object Test extends ScaladocModelTest {
+
+ // Working around the fact that usecases have the form Coll[T] and not Coll[T, U], as required by Map
+ override def code = """
+ /**
+ * @define Coll C[T]
+ */
+ class C[T] {
+ /**
+ * @usecase def foo[T]: $Coll[T]
+ */
+ def foo[T: Numeric]: C[T]
+ }
+
+
+ /**
+ * @define Coll D1[T]
+ */
+ class D[U, T] extends C[T] {
+ protected type D1[Z] = D[U, Z]
+ }
+ """
+
+ // no need for special settings
+ def scaladocSettings = ""
+
+ def testModel(rootPackage: Package) = {
+ // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s))
+ import access._
+
+ // just need to check the member exists, access methods will throw an error if there's a problem
+ assert(rootPackage._class("D")._method("foo").resultType.name == "D[U, T]",
+ rootPackage._class("D")._method("foo").resultType.name + " == D[U, T]")
+ }
+} \ No newline at end of file
diff --git a/test/scaladoc/run/SI-3484.check b/test/scaladoc/run/SI-3484.check
new file mode 100644
index 0000000000..619c56180b
--- /dev/null
+++ b/test/scaladoc/run/SI-3484.check
@@ -0,0 +1 @@
+Done.
diff --git a/test/scaladoc/run/SI-3484.scala b/test/scaladoc/run/SI-3484.scala
new file mode 100644
index 0000000000..297aebee8f
--- /dev/null
+++ b/test/scaladoc/run/SI-3484.scala
@@ -0,0 +1,52 @@
+import scala.tools.nsc.doc.model._
+import scala.tools.nsc.doc.model.diagram._
+import scala.tools.partest.ScaladocModelTest
+
+object Test extends ScaladocModelTest {
+
+ override def code = """
+ class cbf[A, B, C]
+
+ /**
+ * @define Coll Collection
+ * @define bfreturn $Coll
+ */
+ class Collection[A] {
+ /** What map does...
+ *
+ * $bfreturn
+ * @usecase def map[B](f: A => B): $bfreturn[B]
+ *
+ */
+ def map[B, That](f: A => B)(implicit fact: cbf[Collection[A], B, That]) =
+ null
+ }
+
+ /**
+ * @define b John
+ * @define a Mister $b
+ */
+ class SR704 {
+ /**
+ * Hello $a.
+ */
+ def foo = 123
+ }
+ """
+
+ // diagrams must be started. In case there's an error with dot, it should not report anything
+ def scaladocSettings = ""
+
+ def testModel(rootPackage: Package) = {
+ // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s))
+ import access._
+
+ // check correct expansion of the use case signature
+ val map = rootPackage._class("Collection")._method("map")
+ assert(map.resultType.name == "Collection[B]", map.resultType.name + " == Traversable[B]")
+
+ val foo = rootPackage._class("SR704")._method("foo")
+ assert(extractCommentText(foo.comment.get).contains("Hello Mister John."),
+ extractCommentText(foo.comment.get) + ".contains(Hello Mister John.)")
+ }
+} \ No newline at end of file
diff --git a/test/scaladoc/run/SI-4324.check b/test/scaladoc/run/SI-4324.check
new file mode 100644
index 0000000000..619c56180b
--- /dev/null
+++ b/test/scaladoc/run/SI-4324.check
@@ -0,0 +1 @@
+Done.
diff --git a/test/scaladoc/run/SI-4324.scala b/test/scaladoc/run/SI-4324.scala
new file mode 100644
index 0000000000..686a133dc0
--- /dev/null
+++ b/test/scaladoc/run/SI-4324.scala
@@ -0,0 +1,24 @@
+import scala.tools.nsc.doc.model._
+import scala.tools.partest.ScaladocModelTest
+
+object Test extends ScaladocModelTest {
+
+ override def code = """
+ case class Test4324(arg11: String, arg12: Int)(arg21: String, arg22: Int)(arg31: Int, arg32: String)
+ """
+
+ // no need for special settings
+ def scaladocSettings = ""
+
+ def testModel(rootPackage: Package) = {
+ // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s))
+ import access._
+
+ // just need to check the member exists, access methods will throw an error if there's a problem
+ rootPackage._class("Test4324").asInstanceOf[Class].valueParams match {
+ case List(List(arg11, arg12), List(arg21, arg22), List(arg31, arg32)) => //yeeey, do nothing
+ case other =>
+ assert(false, "Incorrect valueParams generated: " + other + " instead of (arg11, arg12)(arg21, arg22)(arg31, arg32)")
+ }
+ }
+} \ No newline at end of file
diff --git a/test/scaladoc/run/SI-4360.check b/test/scaladoc/run/SI-4360.check
new file mode 100644
index 0000000000..619c56180b
--- /dev/null
+++ b/test/scaladoc/run/SI-4360.check
@@ -0,0 +1 @@
+Done.
diff --git a/test/scaladoc/run/SI-4360.scala b/test/scaladoc/run/SI-4360.scala
new file mode 100644
index 0000000000..3abc61c267
--- /dev/null
+++ b/test/scaladoc/run/SI-4360.scala
@@ -0,0 +1,48 @@
+import scala.tools.nsc.doc.model._
+import scala.tools.partest.ScaladocModelTest
+
+object Test extends ScaladocModelTest {
+
+ override def resourceFile = "SI-4360.scala"
+
+ // no need for special settings
+ def scaladocSettings = ""
+
+ def testModel(rootPackage: Package) = {
+ // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s))
+ import access._
+
+ // just need to check the member exists, access methods will throw an error if there's a problem
+ val base = rootPackage._package("scala")._package("test")._package("scaladoc")._package("prefix")
+
+ val TEST = base._package("pack1")._package("c")._class("TEST")
+ val fooCA = TEST._method("fooCA")
+ val fooCB = TEST._method("fooCB")
+ val fooCS = TEST._method("fooCS")
+ val fooCL = TEST._method("fooCL")
+ val fooPA = TEST._method("fooPA")
+ val fooPB = TEST._method("fooPB")
+ val fooPC = TEST._method("fooPC")
+
+ val expected = List(
+ (fooCA, "Z", 1),
+ (fooCB, "B.Z", 1),
+ (fooCS, "pack2.Z.Z", 1),
+ (fooCL, "L.Z", 1),
+ (fooPA, "a.C", 1),
+ (fooPB, "b.C", 1),
+ (fooPC, "C", 1)
+ )
+
+ for ((method, name, refs) <- expected) {
+ assert(method.valueParams(0)(0).resultType.name == name,
+ method.valueParams(0)(0).resultType.name + " == " + name + " (in " + method.qualifiedName + ")")
+ assert(method.valueParams(0)(0).resultType.refEntity.size == refs,
+ method.valueParams(0)(0).resultType.refEntity.size + " == " + refs + " (in " + method.qualifiedName + ")")
+ }
+
+ val A = base._package("pack1")._package("c")._class("A")
+ assert(A.linearizationTypes(0).name == "pack1.A", A.linearizationTypes(0).name + " == pack1.A")
+ assert(A.linearizationTypes(0).refEntity.size == 1, A.linearizationTypes(0).refEntity.size + " == 1")
+ }
+} \ No newline at end of file
diff --git a/test/scaladoc/run/SI-4676.check b/test/scaladoc/run/SI-4676.check
new file mode 100644
index 0000000000..619c56180b
--- /dev/null
+++ b/test/scaladoc/run/SI-4676.check
@@ -0,0 +1 @@
+Done.
diff --git a/test/scaladoc/run/SI-4676.scala b/test/scaladoc/run/SI-4676.scala
new file mode 100644
index 0000000000..b83a59a472
--- /dev/null
+++ b/test/scaladoc/run/SI-4676.scala
@@ -0,0 +1,26 @@
+import scala.tools.nsc.doc.model._
+import scala.tools.nsc.doc.model.diagram._
+import scala.tools.partest.ScaladocModelTest
+
+object Test extends ScaladocModelTest {
+
+ override def code = """
+ class SI_4676 {
+ type SS = (String,String)
+ def x(ss: SS): Int = 3
+ }
+ class cbf[A, B, C]
+ """
+
+ // diagrams must be started. In case there's an error with dot, it should not report anything
+ def scaladocSettings = ""
+
+ def testModel(rootPackage: Package) = {
+ // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s))
+ import access._
+
+ // check correct expansion of the use case signature
+ val x = rootPackage._class("SI_4676")._method("x")
+ assert(x.valueParams(0)(0).resultType.name == "(String, String)", "parameter ss of method x has type (String, String")
+ }
+} \ No newline at end of file
diff --git a/test/scaladoc/run/SI-4887.check b/test/scaladoc/run/SI-4887.check
new file mode 100644
index 0000000000..619c56180b
--- /dev/null
+++ b/test/scaladoc/run/SI-4887.check
@@ -0,0 +1 @@
+Done.
diff --git a/test/scaladoc/run/SI-4887.scala b/test/scaladoc/run/SI-4887.scala
new file mode 100644
index 0000000000..af83344613
--- /dev/null
+++ b/test/scaladoc/run/SI-4887.scala
@@ -0,0 +1,46 @@
+import scala.tools.nsc.doc.model._
+import scala.tools.partest.ScaladocModelTest
+
+object Test extends ScaladocModelTest {
+
+ override def code = """
+ package scala.test.scaladoc.existentials {
+ import language.higherKinds
+ import language.existentials
+
+ class X[T, U, V]
+
+ trait TEST {
+ type T
+ type U
+ type A
+ def foo1(x: X[T, U, _]) = 3
+ def foo2(x: X[Z[_], U, z.type] forSome {type Z[_] <: { def z: String }; val z: Z[_ <: Int]}) = 4
+ def foo3(x: X[Z, Z, V] forSome { type Z <: T; type V <: T }) = 6
+ }
+ }
+ """
+
+ // no need for special settings
+ def scaladocSettings = "-feature"
+
+ def testModel(rootPackage: Package) = {
+ // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s))
+ import access._
+
+ val base = rootPackage._package("scala")._package("test")._package("scaladoc")._package("existentials")
+ val TEST = base._trait("TEST")
+
+ val foo1 = TEST._method("foo1")
+ assert(foo1.valueParams(0)(0).resultType.name == "X[T, U, _]",
+ foo1.valueParams(0)(0).resultType.name + " == X[T, U, _]")
+
+ val foo2 = TEST._method("foo2")
+ assert(foo2.valueParams(0)(0).resultType.name == "X[Z[_], U, _ <: [_]AnyRef { def z: String } with Singleton]",
+ foo2.valueParams(0)(0).resultType.name + " == X[Z[_], U, _ <: [_]AnyRef { def z: String } with Singleton]")
+
+ val foo3 = TEST._method("foo3")
+ assert(foo3.valueParams(0)(0).resultType.name == "X[Z, Z, V] forSome {type Z <: T, type V <: T}",
+ foo3.valueParams(0)(0).resultType.name + " == X[Z, Z, V] forSome {type Z <: T, type V <: T}")
+ }
+} \ No newline at end of file
diff --git a/test/scaladoc/run/SI-5235.check b/test/scaladoc/run/SI-5235.check
new file mode 100644
index 0000000000..d9acfd063b
--- /dev/null
+++ b/test/scaladoc/run/SI-5235.check
@@ -0,0 +1,4 @@
+newSource:10: warning: Could not find the type $Coll points to while expanding it for the usecase signature of method reverse in trait SpecificColl.In this context, $Coll = "BullSh".
+ * @usecase def reverse(): $Coll
+ ^
+Done.
diff --git a/test/scaladoc/run/SI-5235.scala b/test/scaladoc/run/SI-5235.scala
new file mode 100644
index 0000000000..6295fc7786
--- /dev/null
+++ b/test/scaladoc/run/SI-5235.scala
@@ -0,0 +1,87 @@
+import scala.tools.nsc.doc.model._
+import scala.tools.nsc.doc.model.diagram._
+import scala.tools.partest.ScaladocModelTest
+
+object Test extends ScaladocModelTest {
+
+ override def code = """
+ package scala.test.scaladoc.SI5235 {
+ trait Builder[From, To]
+
+ /**
+ * @define Coll `GenericColl`
+ */
+ class GenericColl {
+ /**
+ * @usecase def reverse(): $Coll
+ * Returns the reversed $Coll.
+ */
+ def reverse[T](implicit something: Builder[GenericColl, T]): T
+ def foo1: GenericColl = ???
+ }
+
+ /** Nooo, don't point to this */
+ trait MyCollection
+
+ package specific {
+ /**
+ * @define Coll `BullSh`
+ */
+ trait SpecificColl extends GenericColl {
+ def foo2: SpecificColl = ???
+ }
+ }
+
+ package mycoll {
+ /**
+ * @define Coll `mycoll.MyCollection`
+ */
+ class MyCollection extends specific.SpecificColl {
+ def foo3: MyCollection = ???
+ }
+ }
+ }
+ """
+
+ // diagrams must be started. In case there's an error with dot, it should not report anything
+ def scaladocSettings = ""
+
+ def testModel(rootPackage: Package) = {
+ // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s))
+ import access._
+
+ val base = rootPackage._package("scala")._package("test")._package("scaladoc")._package("SI5235")
+
+ val GenericColl = base._class("GenericColl")
+ val SpecificColl = base._package("specific")._trait("SpecificColl")
+ val MyCollection = base._package("mycoll")._class("MyCollection")
+
+ // check comment text
+ val gcComment = extractCommentText(GenericColl._method("reverse").comment.get)
+ val scComment = extractCommentText(SpecificColl._method("reverse").comment.get)
+ val mcComment = extractCommentText(MyCollection._method("reverse").comment.get)
+ assert(gcComment.contains("Returns the reversed GenericColl."),
+ gcComment + ".contains(\"Returns the reversed GenericColl.\")")
+ assert(scComment.contains("Returns the reversed BullSh."),
+ scComment + ".contains(\"Returns the reversed BullSh.\")")
+ assert(mcComment.contains("Returns the reversed mycoll.MyCollection."),
+ mcComment + ".contains(\"Returns the reversed mycoll.MyCollection.\")")
+
+ // check signatures
+ val gcReverse = GenericColl._method("reverse")
+ val scReverse = SpecificColl._method("reverse")
+ val mcReverse = MyCollection._method("reverse")
+ val gcReverseType = gcReverse.resultType
+ val scReverseType = scReverse.resultType
+ val mcReverseType = mcReverse.resultType
+ assert(gcReverseType.name == "GenericColl", gcReverseType.name + " == GenericColl")
+ assert(scReverseType.name == "BullSh", scReverseType.name + " == BullSh")
+ assert(mcReverseType.name == "MyCollection",mcReverseType.name + " == MyCollection")
+ assert(gcReverseType.refEntity(0)._1 == LinkToTpl(GenericColl),
+ gcReverse.qualifiedName + "'s return type has a link to " + GenericColl.qualifiedName)
+ assert(scReverseType.refEntity(0)._1 == Tooltip("BullSh"),
+ scReverseType.refEntity(0)._1 + " == Tooltip(\"BullSh\")")
+ assert(mcReverseType.refEntity(0)._1 == LinkToTpl(MyCollection),
+ mcReverse.qualifiedName + "'s return type has a link to " + MyCollection.qualifiedName)
+ }
+} \ No newline at end of file
diff --git a/test/scaladoc/run/SI-5533.check b/test/scaladoc/run/SI-5533.check
new file mode 100644
index 0000000000..619c56180b
--- /dev/null
+++ b/test/scaladoc/run/SI-5533.check
@@ -0,0 +1 @@
+Done.
diff --git a/test/scaladoc/run/SI-5533.scala b/test/scaladoc/run/SI-5533.scala
new file mode 100644
index 0000000000..989d9aa13a
--- /dev/null
+++ b/test/scaladoc/run/SI-5533.scala
@@ -0,0 +1,39 @@
+import scala.tools.nsc.doc.model._
+import scala.tools.partest.ScaladocModelTest
+
+object Test extends ScaladocModelTest {
+
+ // Working around the fact that usecases have the form Coll[T] and not Coll[T, U], as required by Map
+ override def code = """
+ package a {
+ class A { class Z }
+ class C extends b.B { class X extends Y }
+ }
+
+ package b {
+ /** @contentDiagram */
+ class B extends a.A { class Y extends Z }
+ /** @contentDiagram */
+ class D extends a.C { class V extends X }
+ }
+ """
+
+ // no need for special settings
+ def scaladocSettings = "-diagrams -skip-packages a"
+
+ def testModel(rootPackage: Package) = {
+ // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s))
+ import access._
+
+ // just need to check the member exists, access methods will throw an error if there's a problem
+ assert(!rootPackage.templates.exists(_.name == "a"), "package a should not exist in the root package")
+ assert(rootPackage.templates.exists(_.name == "b"), "package b should exist in the root package")
+ val b = rootPackage._package("b")
+ val B = b._class("B")
+ val D = b._class("D")
+ testDiagram(B, B.contentDiagram, 2, 1)
+ // unfortunately not all packages, as B1 extends A.this.A1 and it gets the wrong member -- maybe we should model
+ // things as we do for symbols?
+ testDiagram(D, D.contentDiagram, 3, 2)
+ }
+} \ No newline at end of file
diff --git a/test/scaladoc/run/SI-5784.check b/test/scaladoc/run/SI-5784.check
new file mode 100644
index 0000000000..619c56180b
--- /dev/null
+++ b/test/scaladoc/run/SI-5784.check
@@ -0,0 +1 @@
+Done.
diff --git a/test/scaladoc/run/SI-5784.scala b/test/scaladoc/run/SI-5784.scala
new file mode 100644
index 0000000000..318eb78b2a
--- /dev/null
+++ b/test/scaladoc/run/SI-5784.scala
@@ -0,0 +1,44 @@
+import scala.tools.nsc.doc.model._
+import scala.tools.partest.ScaladocModelTest
+
+object Test extends ScaladocModelTest {
+
+ override def resourceFile: String = "SI-5784.scala"
+
+ // no need for special settings
+ def scaladocSettings = "-diagrams"
+
+ def testModel(rootPackage: Package) = {
+ // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s))
+ import access._
+
+ val main = rootPackage._package("test")._package("templates")
+
+ val String = main._aliasTypeTpl("String")
+ assert(String.companion.isDefined, "test.templates.String should have a pseudo-companion object")
+
+ val Base = main._trait("Base")
+ assert(Base.members.filter(_.inDefinitionTemplates.head == Base).length == 5, Base.members.filter(_.inDefinitionTemplates.head == Base).length + " == 5")
+ assert(Base.members.collect{case d: DocTemplateEntity => d}.length == 4, Base.members.collect{case d: DocTemplateEntity => d}.length == 4)
+ testDiagram(Base, Base.contentDiagram, 2, 1)
+
+ val BaseT = Base._absTypeTpl("T")
+ val Foo = Base._trait("Foo")
+ assert(BaseT.members.filter(_.inDefinitionTemplates.head == Base).length == 0, BaseT.members.filter(_.inDefinitionTemplates.head == Base).length + " == 0")
+ assert(BaseT.members.map(_.name).sorted == Foo.members.map(_.name).sorted, BaseT.members.map(_.name).sorted + " == " + Foo.members.map(_.name).sorted)
+ assert(BaseT.companion.isDefined, "test.templates.Base.T should have a pseudo-companion object")
+ testDiagram(BaseT, BaseT.inheritanceDiagram, 2, 1)
+
+ val Api = main._trait("Api")
+ assert(Api.members.filter(_.inDefinitionTemplates.head == Api).length == 2, Api.members.filter(_.inDefinitionTemplates.head == Api).length + " == 2") // FooApi and override type T
+ assert(Api.members.collect{case d: DocTemplateEntity => d}.length == 5, Api.members.collect{case d: DocTemplateEntity => d}.length == 5)
+ testDiagram(Api, Api.contentDiagram, 3, 2)
+
+ val ApiT = Api._absTypeTpl("T")
+ val FooApi = Api._trait("FooApi")
+ assert(ApiT.members.filter(_.inDefinitionTemplates.head == Api).length == 0, ApiT.members.filter(_.inDefinitionTemplates.head == Api).length + " == 0")
+ assert(ApiT.members.map(_.name).sorted == FooApi.members.map(_.name).sorted, ApiT.members.map(_.name).sorted + " == " + FooApi.members.map(_.name).sorted)
+ assert(ApiT.companion.isDefined, "test.templates.Api.T should have a pseudo-companion object")
+ testDiagram(ApiT, ApiT.inheritanceDiagram, 2, 1)
+ }
+} \ No newline at end of file
diff --git a/test/scaladoc/run/SI-5965.check b/test/scaladoc/run/SI-5965.check
new file mode 100644
index 0000000000..619c56180b
--- /dev/null
+++ b/test/scaladoc/run/SI-5965.check
@@ -0,0 +1 @@
+Done.
diff --git a/test/scaladoc/run/SI-5965.scala b/test/scaladoc/run/SI-5965.scala
new file mode 100644
index 0000000000..6f4540d239
--- /dev/null
+++ b/test/scaladoc/run/SI-5965.scala
@@ -0,0 +1,24 @@
+import scala.tools.nsc.doc.model._
+import scala.tools.partest.ScaladocModelTest
+
+object Test extends ScaladocModelTest {
+
+ override def code = """
+ abstract class Param
+ class Test
+ object Test {
+ def apply(i: Int): Test = new Test
+ def apply(i: Int, p: Param = new Param { }): Test = new Test
+ }
+ """
+
+ // no need for special settings
+ def scaladocSettings = ""
+
+ def testModel(rootPackage: Package) = {
+ import access._
+
+ // just need to make sure the model exists
+ val base = rootPackage._object("Test")
+ }
+} \ No newline at end of file
diff --git a/test/scaladoc/run/diagrams-base.scala b/test/scaladoc/run/diagrams-base.scala
index 38bed06502..b7aeed51d2 100644
--- a/test/scaladoc/run/diagrams-base.scala
+++ b/test/scaladoc/run/diagrams-base.scala
@@ -42,7 +42,7 @@ object Test extends ScaladocModelTest {
assert(diag.nodes.filter(_.isThisNode).length == 1)
// 1. check class E diagram
- assert(diag.isClassDiagram)
+ assert(diag.isInheritanceDiagram)
val (incoming, outgoing) = diag.edges.partition(!_._1.isThisNode)
assert(incoming.length == 5)
@@ -56,14 +56,14 @@ object Test extends ScaladocModelTest {
assert(incomingSubclass.length == 2)
assert(incomingImplicit.length == 3)
- val classDiag = diag.asInstanceOf[ClassDiagram]
+ val classDiag = diag.asInstanceOf[InheritanceDiagram]
assert(classDiag.incomingImplicits.length == 3)
assert(classDiag.outgoingImplicits.length == 1)
// 2. check package diagram
// NOTE: Z should be eliminated because it's isolated
val packDiag = base.contentDiagram.get
- assert(packDiag.isPackageDiagram)
+ assert(packDiag.isContentDiagram)
assert(packDiag.nodes.length == 8) // check singular object removal
assert(packDiag.edges.length == 4)
assert(packDiag.edges.foldLeft(0)(_ + _._2.length) == 6)
diff --git a/test/scaladoc/run/diagrams-determinism.scala b/test/scaladoc/run/diagrams-determinism.scala
index 6c8db05d78..2b6f8eecc7 100644
--- a/test/scaladoc/run/diagrams-determinism.scala
+++ b/test/scaladoc/run/diagrams-determinism.scala
@@ -49,19 +49,19 @@ object Test extends ScaladocModelTest {
assert(run2 == run3)
// 2. check the order in the diagram: this node, subclasses, and then implicit conversions
- def assertRightOrder(diagram: Diagram) = {
+ def assertRightOrder(template: DocTemplateEntity, diagram: Diagram) =
for ((node, subclasses) <- diagram.edges)
assert(subclasses == subclasses.filter(_.isThisNode) :::
- subclasses.filter(_.isNormalNode) :::
- subclasses.filter(_.isImplicitNode))
- }
+ subclasses.filter(node => node.isNormalNode || node.isOutsideNode) :::
+ subclasses.filter(_.isImplicitNode),
+ "Diagram order for " + template + ": " + subclasses)
val base = rootPackage._package("scala")._package("test")._package("scaladoc")._package("diagrams")
- assertRightOrder(base.contentDiagram.get)
- assertRightOrder(base._trait("A").inheritanceDiagram.get)
- assertRightOrder(base._trait("B").inheritanceDiagram.get)
- assertRightOrder(base._trait("C").inheritanceDiagram.get)
- assertRightOrder(base._trait("D").inheritanceDiagram.get)
- assertRightOrder(base._trait("E").inheritanceDiagram.get)
+ assertRightOrder(base, base.contentDiagram.get)
+ assertRightOrder(base._trait("A"), base._trait("A").inheritanceDiagram.get)
+ assertRightOrder(base._trait("B"), base._trait("B").inheritanceDiagram.get)
+ assertRightOrder(base._trait("C"), base._trait("C").inheritanceDiagram.get)
+ assertRightOrder(base._trait("D"), base._trait("D").inheritanceDiagram.get)
+ assertRightOrder(base._trait("E"), base._trait("E").inheritanceDiagram.get)
}
} \ No newline at end of file
diff --git a/test/scaladoc/run/diagrams-filtering.scala b/test/scaladoc/run/diagrams-filtering.scala
index 8cb32180a1..54e3e9ac63 100644
--- a/test/scaladoc/run/diagrams-filtering.scala
+++ b/test/scaladoc/run/diagrams-filtering.scala
@@ -65,7 +65,7 @@ object Test extends ScaladocModelTest {
assert(!C.inheritanceDiagram.isDefined)
// trait G
- val G = base._trait("G")
+ val G = base._class("G")
assert(!G.inheritanceDiagram.isDefined)
// trait E
diff --git a/test/scaladoc/run/groups.check b/test/scaladoc/run/groups.check
new file mode 100644
index 0000000000..619c56180b
--- /dev/null
+++ b/test/scaladoc/run/groups.check
@@ -0,0 +1 @@
+Done.
diff --git a/test/scaladoc/run/groups.scala b/test/scaladoc/run/groups.scala
new file mode 100644
index 0000000000..05324c2ec9
--- /dev/null
+++ b/test/scaladoc/run/groups.scala
@@ -0,0 +1,119 @@
+import scala.tools.nsc.doc.model._
+import scala.tools.partest.ScaladocModelTest
+
+object Test extends ScaladocModelTest {
+
+ override def code = """
+ package test.scaladoc.groups {
+
+ /**
+ * The trait A
+ * @groupdesc A Group A is the group that contains functions starting with f
+ * For example:
+ * {{{
+ * this is an example
+ * }}}
+ * @groupdesc B Group B is the group that contains functions starting with b
+ * @groupname B Group B has a nice new name and a high priority
+ * @groupprio B -10
+ * @group Traits
+ * @note This is a note
+ */
+ trait A {
+ /** foo description
+ * @group A */
+ def foo = 1
+
+ /** bar description
+ * @group B */
+ def bar = 2
+ }
+
+ /** The trait B
+ * @group Traits
+ * @groupdesc C Group C is introduced by B
+ */
+ trait B {
+ /** baz descriptopn
+ * @group C */
+ def baz = 3
+ }
+
+ /** The class C which inherits from both A and B
+ * @group Classes
+ * @groupdesc B Look ma, I'm overriding group descriptions!!!
+ * @groupname B And names
+ */
+ class C extends A with B {
+ /** Oh noes, I lost my group -- or did I?!? */
+ override def baz = 4
+ }
+ }
+ """
+
+ // no need for special settings
+ def scaladocSettings = "-feature"
+
+ def testModel(rootPackage: Package) = {
+ // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s))
+ import access._
+
+ // just need to check the member exists, access methods will throw an error if there's a problem
+ val base = rootPackage._package("test")._package("scaladoc")._package("groups")
+
+ def checkGroup(mbr: MemberEntity, grp: String) =
+ assert(mbr.group == grp, "Incorrect group for " + mbr.qualifiedName + ": " + mbr.group + " instead of " + grp)
+
+ def checkGroupDesc(dtpl: DocTemplateEntity, grp: String, grpDesc: String) = {
+ assert(dtpl.groupDescription(grp).isDefined,
+ "Group description for " + grp + " not defined in " + dtpl.qualifiedName)
+ assert(extractCommentText(dtpl.groupDescription(grp).get).contains(grpDesc),
+ "Group description for " + grp + " in " + dtpl.qualifiedName + " does not contain \"" + grpDesc + "\": \"" +
+ extractCommentText(dtpl.groupDescription(grp).get) + "\"")
+ }
+
+ def checkGroupName(dtpl: DocTemplateEntity, grp: String, grpName: String) =
+ // TODO: See why we need trim here, we already do trimming in the CommentFactory
+ assert(dtpl.groupName(grp) == grpName,
+ "Group name for " + grp + " in " + dtpl.qualifiedName + " does not equal \"" + grpName + "\": \"" + dtpl.groupName(grp) + "\"")
+
+ def checkGroupPrio(dtpl: DocTemplateEntity, grp: String, grpPrio: Int) =
+ assert(dtpl.groupPriority(grp) == grpPrio,
+ "Group priority for " + grp + " in " + dtpl.qualifiedName + " does not equal " + grpPrio + ": " + dtpl.groupPriority(grp))
+
+
+ val A = base._trait("A")
+ val B = base._trait("B")
+ val C = base._class("C")
+ checkGroup(A, "Traits")
+ checkGroup(B, "Traits")
+ checkGroup(C, "Classes")
+ checkGroup(A._method("foo"), "A")
+ checkGroup(A._method("bar"), "B")
+ checkGroup(B._method("baz"), "C")
+ checkGroup(C._method("foo"), "A")
+ checkGroup(C._method("bar"), "B")
+ checkGroup(C._method("baz"), "C")
+
+ checkGroupDesc(A, "A", "Group A is the group that contains functions starting with f")
+ checkGroupName(A, "A", "A")
+ checkGroupPrio(A, "A", 0)
+ checkGroupDesc(A, "B", "Group B is the group that contains functions starting with b")
+ checkGroupName(A, "B", "Group B has a nice new name and a high priority")
+ checkGroupPrio(A, "B", -10)
+
+ checkGroupDesc(B, "C", "Group C is introduced by B")
+ checkGroupName(B, "C", "C")
+ checkGroupPrio(B, "C", 0)
+
+ checkGroupDesc(C, "A", "Group A is the group that contains functions starting with f")
+ checkGroupName(C, "A", "A")
+ checkGroupPrio(C, "A", 0)
+ checkGroupDesc(C, "B", "Look ma, I'm overriding group descriptions!!!")
+ checkGroupName(C, "B", "And names")
+ checkGroupPrio(C, "B", -10)
+ checkGroupDesc(C, "C", "Group C is introduced by B")
+ checkGroupName(C, "C", "C")
+ checkGroupPrio(C, "C", 0)
+ }
+} \ No newline at end of file
diff --git a/test/scaladoc/run/implicits-ambiguating.scala b/test/scaladoc/run/implicits-ambiguating.scala
index 1420593b74..05daf1f805 100644
--- a/test/scaladoc/run/implicits-ambiguating.scala
+++ b/test/scaladoc/run/implicits-ambiguating.scala
@@ -17,7 +17,7 @@ object Test extends ScaladocModelTest {
mbr.byConversion.map(_.source.implicitsShadowing.get(mbr).map(_.isAmbiguous).getOrElse(false)).getOrElse(false)
// SEE THE test/resources/implicits-chaining-res.scala FOR THE EXPLANATION OF WHAT'S CHECKED HERE:
- val base = root._package("scala")._package("test")._package("scaladoc")._package("implicits")._object("ambiguating")
+ val base = root._package("scala")._package("test")._package("scaladoc")._package("implicits")._package("ambiguating")
var conv1: ImplicitConversion = null
var conv2: ImplicitConversion = null
diff --git a/test/scaladoc/run/implicits-known-type-classes.check b/test/scaladoc/run/implicits-known-type-classes.check
new file mode 100644
index 0000000000..619c56180b
--- /dev/null
+++ b/test/scaladoc/run/implicits-known-type-classes.check
@@ -0,0 +1 @@
+Done.
diff --git a/test/scaladoc/run/implicits-known-type-classes.scala b/test/scaladoc/run/implicits-known-type-classes.scala
new file mode 100644
index 0000000000..9f4ca372b0
--- /dev/null
+++ b/test/scaladoc/run/implicits-known-type-classes.scala
@@ -0,0 +1,33 @@
+import scala.tools.nsc.doc.model._
+import scala.tools.partest.ScaladocModelTest
+import language._
+
+object Test extends ScaladocModelTest {
+
+ // test a file instead of a piece of code
+ override def resourceFile = "implicits-known-type-classes-res.scala"
+
+ // start implicits
+ def scaladocSettings = "-implicits"
+
+ def testModel(root: Package) = {
+ // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s))
+ import access._
+
+ /** Tests the "known type classes" feature of scaladoc implicits
+ * if the test fails, please update the correct qualified name of
+ * the type class in src/compiler/scala/tools/nsc/doc/Settings.scala
+ * in the knownTypeClasses map. Thank you! */
+
+ val base = root._package("scala")._package("test")._package("scaladoc")._package("implicits")._package("typeclasses")
+ var conv: ImplicitConversion = null
+
+ val A = base._class("A")
+
+ for (conversion <- A.conversions if !conversion.isCommonConversion) {
+ assert(conversion.constraints.length == 1, conversion.constraints.length + " == 1 (in " + conversion + ")")
+ assert(conversion.constraints.head.isInstanceOf[KnownTypeClassConstraint],
+ conversion.constraints.head + " is not a known type class constraint!")
+ }
+ }
+}
diff --git a/test/scaladoc/run/implicits-scopes.scala b/test/scaladoc/run/implicits-scopes.scala
index 7b9e80e148..d91deba326 100644
--- a/test/scaladoc/run/implicits-scopes.scala
+++ b/test/scaladoc/run/implicits-scopes.scala
@@ -24,7 +24,7 @@ object Test extends ScaladocModelTest {
val test1 = base._package("test1")
val A = test1._class("A")
- conv = A._conversion(test1.qualifiedName + ".package.toB") // the .package means it's the package object
+ conv = A._conversion(test1.qualifiedName + ".toB")
assert(conv.members.length == 1)
assert(conv.constraints.length == 0)
}
@@ -36,7 +36,9 @@ object Test extends ScaladocModelTest {
val classes = test2._package("classes")
val A = classes._class("A")
- assert(A._conversions(test2.qualifiedName + ".toB").isEmpty)
+ conv = A._conversion(test2.qualifiedName + ".toB")
+ assert(conv.members.length == 1)
+ assert(conv.constraints.length == 0)
}
//// test3 /////////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/test/scaladoc/run/implicits-shadowing.scala b/test/scaladoc/run/implicits-shadowing.scala
index 2827d31122..6869b12a94 100644
--- a/test/scaladoc/run/implicits-shadowing.scala
+++ b/test/scaladoc/run/implicits-shadowing.scala
@@ -17,7 +17,7 @@ object Test extends ScaladocModelTest {
mbr.byConversion.map(_.source.implicitsShadowing.get(mbr).map(_.isShadowed).getOrElse(false)).getOrElse(false)
// SEE THE test/resources/implicits-chaining-res.scala FOR THE EXPLANATION OF WHAT'S CHECKED HERE:
- val base = root._package("scala")._package("test")._package("scaladoc")._package("implicits")._object("shadowing")
+ val base = root._package("scala")._package("test")._package("scaladoc")._package("implicits")._package("shadowing")
var conv: ImplicitConversion = null
//// class A ///////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/test/scaladoc/run/implicits-var-exp.scala b/test/scaladoc/run/implicits-var-exp.scala
index 16569fe3c2..94d2990d29 100644
--- a/test/scaladoc/run/implicits-var-exp.scala
+++ b/test/scaladoc/run/implicits-var-exp.scala
@@ -6,25 +6,36 @@ object Test extends ScaladocModelTest {
override def code = """
package scala.test.scaladoc.variable.expansion {
- /**
- * Blah blah blah
- */
+ /** @define coll WROOOONG-A */
class A
object A {
import language.implicitConversions
- implicit def aToB(a: A) = new B
+ implicit def aToC(a: A) = new C
+ implicit def aToE(a: A) = new E with F
}
- /**
- * @define coll collection
- */
+ /** @define coll WROOOONG-B */
class B {
- /**
- * foo returns a $coll
- */
+ /** foo returns a $coll */
def foo: Nothing = ???
}
+
+ /** @define coll collection */
+ class C extends B
+
+ /** @define coll WROOOONG-D */
+ trait D {
+ /** bar returns a $coll */
+ def bar: Nothing = ???
+ }
+
+ /** @define coll result */
+ //trait E { self: D => override def bar: Nothing = ??? }
+ trait E extends D { override def bar: Nothing = ??? }
+
+ /** @define coll WROOOONG-F */
+ trait F
}
"""
@@ -37,7 +48,9 @@ object Test extends ScaladocModelTest {
val base = rootPackage._package("scala")._package("test")._package("scaladoc")._package("variable")._package("expansion")
val foo = base._class("A")._method("foo")
-
assert(foo.comment.get.body.toString.contains("foo returns a collection"), "\"" + foo.comment.get.body.toString + "\".contains(\"foo returns a collection\")")
+
+ val bar = base._class("A")._method("bar")
+ assert(bar.comment.get.body.toString.contains("bar returns a result"), "\"" + bar.comment.get.body.toString + "\".contains(\"bar returns a result\")")
}
} \ No newline at end of file
diff --git a/test/scaladoc/run/links.check b/test/scaladoc/run/links.check
new file mode 100644
index 0000000000..619c56180b
--- /dev/null
+++ b/test/scaladoc/run/links.check
@@ -0,0 +1 @@
+Done.
diff --git a/test/scaladoc/run/links.scala b/test/scaladoc/run/links.scala
new file mode 100644
index 0000000000..40ce6368ce
--- /dev/null
+++ b/test/scaladoc/run/links.scala
@@ -0,0 +1,28 @@
+import scala.tools.nsc.doc.model._
+import scala.tools.partest.ScaladocModelTest
+
+// SI-5079 "Scaladoc can't link to an object (only a class or trait)"
+// SI-4497 "Links in ScalaDoc - Spec and implementation unsufficient"
+// SI-4224 "Wiki-links should support method targets"
+// SI-3695 "support non-fully-qualified type links in scaladoc comments"
+object Test extends ScaladocModelTest {
+
+ override def resourceFile = "links.scala"
+
+ // no need for special settings
+ def scaladocSettings = ""
+
+ def testModel(rootPackage: Package) = {
+ // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s))
+ import access._
+
+ // just need to check the member exists, access methods will throw an error if there's a problem
+ val base = rootPackage._package("scala")._package("test")._package("scaladoc")._package("links")
+ val TEST = base._object("TEST")
+
+ 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)")
+ }
+} \ No newline at end of file
diff --git a/test/scaladoc/scalacheck/CommentFactoryTest.scala b/test/scaladoc/scalacheck/CommentFactoryTest.scala
index b576ba5544..5e3141bdc0 100644
--- a/test/scaladoc/scalacheck/CommentFactoryTest.scala
+++ b/test/scaladoc/scalacheck/CommentFactoryTest.scala
@@ -10,7 +10,13 @@ import scala.tools.nsc.doc.model.diagram._
class Factory(val g: Global, val s: doc.Settings)
extends doc.model.ModelFactory(g, s) {
- thisFactory: Factory with ModelFactoryImplicitSupport with DiagramFactory with CommentFactory with doc.model.TreeFactory =>
+ thisFactory: Factory
+ with ModelFactoryImplicitSupport
+ with ModelFactoryTypeSupport
+ with DiagramFactory
+ with CommentFactory
+ with doc.model.TreeFactory
+ with MemberLookup =>
def strip(c: Comment): Option[Inline] = {
c.body match {
@@ -31,7 +37,13 @@ object Test extends Properties("CommentFactory") {
val settings = new doc.Settings((str: String) => {})
val reporter = new scala.tools.nsc.reporters.ConsoleReporter(settings)
val g = new Global(settings, reporter)
- (new Factory(g, settings) with ModelFactoryImplicitSupport with DiagramFactory with CommentFactory with doc.model.TreeFactory)
+ (new Factory(g, settings)
+ with ModelFactoryImplicitSupport
+ with ModelFactoryTypeSupport
+ with DiagramFactory
+ with CommentFactory
+ with doc.model.TreeFactory
+ with MemberLookup)
}
def parse(src: String, dst: Inline) = {
diff --git a/test/scaladoc/scalacheck/HtmlFactoryTest.scala b/test/scaladoc/scalacheck/HtmlFactoryTest.scala
index 5b6f75426e..13eacf79a5 100644
--- a/test/scaladoc/scalacheck/HtmlFactoryTest.scala
+++ b/test/scaladoc/scalacheck/HtmlFactoryTest.scala
@@ -235,30 +235,6 @@ object Test extends Properties("HtmlFactory") {
}
}
- property("Trac #3484") = {
- val files = createTemplates("Trac3484.scala")
-
- files("Collection.html") match {
- case node: scala.xml.Node => {
- val s = node.toString
- s.contains("""<span class="result">: Traversable[B]</span>""")
- }
- case _ => false
- }
- }
-
- property("Trac #3484 - SR704") = {
- val files = createTemplates("Trac3484.scala")
-
- files("SR704.html") match {
- case node: scala.xml.Node => {
- val s = node.toString
- s.contains("Hello Mister John.")
- }
- case _ => false
- }
- }
-
property("Trac #4325 - files") = {
val files = createTemplates("Trac4325.scala")
@@ -303,7 +279,7 @@ object Test extends Properties("HtmlFactory") {
case _ => false
}
}
- //
+ //
// property("Trac #484 - refinements and existentials") = {
// val files = createTemplates("Trac484.scala")
// val lines = """
@@ -315,7 +291,7 @@ object Test extends Properties("HtmlFactory") {
// |def j(x: Int): Bar
// |def k(): AnyRef { type Dingus <: T forSome { type T <: String } }
// """.stripMargin.trim.lines map (_.trim)
- //
+ //
// files("RefinementAndExistentials.html") match {
// case node: scala.xml.Node => {
// val s = node.text.replaceAll("\\s+", " ")
@@ -397,26 +373,17 @@ object Test extends Properties("HtmlFactory") {
}
}
- property("Should decode symbolic type alias name.") = {
+ property("SI-4714: Should decode symbolic type alias name.") = {
createTemplate("SI_4715.scala") match {
case node: scala.xml.Node => {
val html = node.toString
- html.contains(">: :+:[<")
- }
- case _ => false
- }
- }
-
- property("Shouldn't drop type arguments to aliased tuple.") = {
- createTemplate("SI_4676.scala") match {
- case node: scala.xml.Node => {
- node.toString.contains(">ss: (String, String)<")
+ html.contains(">:+:<")
}
case _ => false
}
}
- property("Default arguments of synthesized constructor") = {
+ property("SI-4287: Default arguments of synthesized constructor") = {
val files = createTemplates("SI_4287.scala")
files("ClassWithSugar.html") match {
@@ -427,7 +394,7 @@ object Test extends Properties("HtmlFactory") {
}
}
- property("Default arguments of synthesized constructor") = {
+ property("SI-4507: Default arguments of synthesized constructor") = {
createTemplate("SI_4507.scala") match {
case node: scala.xml.Node =>
! node.toString.contains("<li>returns silently when evaluating true and true</li>")
@@ -435,40 +402,40 @@ object Test extends Properties("HtmlFactory") {
}
}
- property("Use cases and links should not crash scaladoc") = {
+ property("SI-4898: Use cases and links should not crash scaladoc") = {
createTemplate("SI_4898.scala")
true
}
- property("Use cases should override their original members") =
+ property("SI-5054: Use cases should override their original members") =
checkText("SI_5054_q1.scala")(
(None,"""def test(): Int""", true)
//Disabled because the full signature is now displayed
//(None,"""def test(implicit lost: Int): Int""", false)
)
- property("Use cases should keep their flags - final should not be lost") =
+ property("SI-5054: Use cases should keep their flags - final should not be lost") =
checkText("SI_5054_q2.scala")((None, """final def test(): Int""", true))
- property("Use cases should keep their flags - implicit should not be lost") =
+ property("SI-5054: Use cases should keep their flags - implicit should not be lost") =
checkText("SI_5054_q3.scala")((None, """implicit def test(): Int""", true))
- property("Use cases should keep their flags - real abstract should not be lost") =
+ property("SI-5054: Use cases should keep their flags - real abstract should not be lost") =
checkText("SI_5054_q4.scala")((None, """abstract def test(): Int""", true))
- property("Use cases should keep their flags - traits should not be affected") =
+ property("SI-5054: Use cases should keep their flags - traits should not be affected") =
checkText("SI_5054_q5.scala")((None, """def test(): Int""", true))
- property("Use cases should keep their flags - traits should not be affected") =
+ property("SI-5054: Use cases should keep their flags - traits should not be affected") =
checkText("SI_5054_q6.scala")((None, """abstract def test(): Int""", true))
- property("Use case individual signature test") =
+ property("SI-5054: Use case individual signature test") =
checkText("SI_5054_q7.scala")(
(None, """abstract def test2(explicit: Int): Int [use case] This takes the explicit value passed.""", true),
(None, """abstract def test1(): Int [use case] This takes the implicit value in scope.""", true)
)
- property("Display correct \"Definition classes\"") =
+ property("SI-5287: Display correct \"Definition classes\"") =
checkText("SI_5287.scala")(
(None,
"""def method(): Int
@@ -477,7 +444,7 @@ object Test extends Properties("HtmlFactory") {
Definition Classes SI_5287 SI_5287_B SI_5287_A""", true)
) // the explanation appears twice, as small comment and full comment
- property("Correct comment inheritance for overriding") =
+ property("Comment inheritance: Correct comment inheritance for overriding") =
checkText("implicit-inheritance-override.scala")(
(Some("Base"),
"""def function[T](arg1: T, arg2: String): Double
@@ -521,7 +488,7 @@ object Test extends Properties("HtmlFactory") {
)
for (useCaseFile <- List("UseCaseInheritance", "UseCaseOverrideInheritance")) {
- property("Correct comment inheritance for usecases") =
+ property("Comment inheritance: Correct comment inheritance for usecases") =
checkText("implicit-inheritance-usecase.scala")(
(Some(useCaseFile),
"""def missing_arg[T](arg1: T): Double
@@ -588,7 +555,7 @@ object Test extends Properties("HtmlFactory") {
)
}
- property("Correct explicit inheritance for override") =
+ property("Comment inheritance: Correct explicit inheritance for override") =
checkText("explicit-inheritance-override.scala")(
(Some("InheritDocDerived"),
"""def function[T](arg1: T, arg2: String): Double
@@ -614,7 +581,7 @@ object Test extends Properties("HtmlFactory") {
See also StartSee The Manual EndSee
""", true))
- property("Correct explicit inheritance for usecase") =
+ property("Comment inheritance: Correct explicit inheritance for usecase") =
checkText("explicit-inheritance-usecase.scala")(
(Some("UseCaseInheritDoc"),
"""def function[T](arg1: T, arg2: String): Double
@@ -639,7 +606,7 @@ object Test extends Properties("HtmlFactory") {
See also StartSee The Manual EndSee
""", true))
- property("Correct explicit inheritance in corner cases") =
+ property("Comment inheritance: Correct explicit inheritance in corner cases") =
checkText("inheritdoc-corner-cases.scala")(
(Some("D"),
"""def hello1: Int
diff --git a/test/scaladoc/scalacheck/IndexTest.scala b/test/scaladoc/scalacheck/IndexTest.scala
index 29e337da2b..bf385898fc 100644
--- a/test/scaladoc/scalacheck/IndexTest.scala
+++ b/test/scaladoc/scalacheck/IndexTest.scala
@@ -16,7 +16,7 @@ object Test extends Properties("Index") {
val morepaths = Thread.currentThread.getContextClassLoader.getParent.asInstanceOf[URLClassLoader].getURLs.map(u => URLDecoder.decode(u.getPath))
(paths ++ morepaths).mkString(java.io.File.pathSeparator)
}
-
+
val docFactory = {
val settings = new doc.Settings({Console.err.println(_)})
@@ -27,9 +27,9 @@ object Test extends Properties("Index") {
new doc.DocFactory(reporter, settings)
}
-
+
val indexModelFactory = doc.model.IndexModelFactory
-
+
def createIndex(path: String): Option[Index] = {
val maybeUniverse = {
@@ -79,4 +79,11 @@ object Test extends Properties("Index") {
case None => false
}
}
+ property("package objects in index") = {
+ createIndex("test/scaladoc/resources/SI-5558.scala") match {
+ case Some(index) =>
+ index.index.firstLetterIndex('f') isDefinedAt "foo"
+ case None => false
+ }
+ }
}