[scaladoc] Optional link to source (set parameter "-doc-source-url"). Support for commenting packages (using package objects). Contributed by Perdo Furlanetto. Also: small performance improvements, short comment extraction is more robust (but no HTML tags allowed in first sentence), small code clean-ups. Checked by dubochet, no review.
git-svn-id: http://lampsvn.epfl.ch/svn-repos/scala/scala/trunk@20778 5e8d7ff9-d8ef-0310-90f0-a4852d11357a
This commit is contained in:
parent
800326e386
commit
524440deee
11
build.xml
11
build.xml
|
@ -1269,7 +1269,10 @@ DOCUMENTATION
|
|||
<mkdir dir="${build-docs.dir}/library"/>
|
||||
<scaladoc
|
||||
destdir="${build-docs.dir}/library"
|
||||
doctitle="Scala ${version.number} API"
|
||||
doctitle="Scala Standard Libray"
|
||||
docversion="${version.number}"
|
||||
docsourceurl="https://lampsvn.epfl.ch/trac/scala/browser/scala/trunk/src/"
|
||||
sourcepath="${src.dir}"
|
||||
classpathref="pack.classpath">
|
||||
<src>
|
||||
<files includes="${src.dir}/actors"/>
|
||||
|
@ -1289,6 +1292,7 @@ DOCUMENTATION
|
|||
<exclude name="runtime/ScalaRunTime.scala"/>
|
||||
<exclude name="runtime/StreamCons.scala"/>
|
||||
<exclude name="runtime/StringAdd.scala"/>
|
||||
<exclude name="scala/swing/test/**"/>
|
||||
</scaladoc>
|
||||
<touch file="${build-docs.dir}/library.complete" verbose="no"/>
|
||||
<stopwatch name="docs.lib.timer" action="total"/>
|
||||
|
@ -1351,7 +1355,10 @@ DOCUMENTATION
|
|||
<mkdir dir="${build-docs.dir}/compiler"/>
|
||||
<scaladoc
|
||||
destdir="${build-docs.dir}/compiler"
|
||||
doctitle="Scala Compiler ${version.number} API"
|
||||
doctitle="Scala Compiler"
|
||||
docversion="${version.number}"
|
||||
docsourceurl="https://lampsvn.epfl.ch/trac/scala/browser/scala/trunk/src/"
|
||||
sourcepath="${src.dir}"
|
||||
classpathref="pack.classpath"
|
||||
srcdir="${src.dir}/compiler">
|
||||
<include name="**/*.scala"/>
|
||||
|
|
|
@ -107,6 +107,9 @@ class Scaladoc extends MatchingTask {
|
|||
/** The document version, to be added to the title. */
|
||||
private var docversion: Option[String] = None
|
||||
|
||||
/** Instruct the compiler to generate links to sources */
|
||||
private var docsourceurl: Option[String] = None
|
||||
|
||||
/** Instruct the compiler to use additional parameters */
|
||||
private var addParams: String = ""
|
||||
|
||||
|
@ -264,6 +267,22 @@ class Scaladoc extends MatchingTask {
|
|||
encoding = Some(input)
|
||||
}
|
||||
|
||||
/** Sets the <code>docversion</code> attribute.
|
||||
*
|
||||
* @param input The value of <code>docversion</code>.
|
||||
*/
|
||||
def setDocversion(input: String) {
|
||||
docversion = Some(input)
|
||||
}
|
||||
|
||||
/** Sets the <code>docsourceurl</code> attribute.
|
||||
*
|
||||
* @param input The value of <code>docsourceurl</code>.
|
||||
*/
|
||||
def setDocsourceurl(input: String) {
|
||||
docsourceurl = Some(input)
|
||||
}
|
||||
|
||||
/** Sets the <code>doctitle</code> attribute.
|
||||
*
|
||||
* @param input The value of <code>doctitle</code>.
|
||||
|
@ -497,6 +516,7 @@ class Scaladoc extends MatchingTask {
|
|||
if (!encoding.isEmpty) docSettings.encoding.value = encoding.get
|
||||
if (!doctitle.isEmpty) docSettings.doctitle.value = decodeEscapes(doctitle.get)
|
||||
if (!docversion.isEmpty) docSettings.docversion.value = decodeEscapes(docversion.get)
|
||||
if (!docsourceurl.isEmpty) docSettings.docsourceurl.value =decodeEscapes(docsourceurl.get)
|
||||
docSettings.deprecation.value = deprecation
|
||||
docSettings.unchecked.value = unchecked
|
||||
log("Scaladoc params = '" + addParams + "'", Project.MSG_DEBUG)
|
||||
|
|
|
@ -2469,11 +2469,15 @@ self =>
|
|||
val stats = new ListBuffer[Tree]
|
||||
while (in.token != RBRACE && in.token != EOF) {
|
||||
if (in.token == PACKAGE) {
|
||||
in.flushDoc
|
||||
val start = in.skipToken()
|
||||
stats += {
|
||||
if (in.token == OBJECT) makePackageObject(start, objectDef(in.offset, NoMods))
|
||||
else packaging(start)
|
||||
stats ++= {
|
||||
if (in.token == OBJECT) {
|
||||
joinComment(List(makePackageObject(start, objectDef(in.offset, NoMods))))
|
||||
}
|
||||
else {
|
||||
in.flushDoc
|
||||
List(packaging(start))
|
||||
}
|
||||
}
|
||||
} else if (in.token == IMPORT) {
|
||||
in.flushDoc
|
||||
|
@ -2631,15 +2635,15 @@ self =>
|
|||
while (in.token == SEMI) in.nextToken()
|
||||
val start = in.offset
|
||||
if (in.token == PACKAGE) {
|
||||
in.flushDoc
|
||||
in.nextToken()
|
||||
if (in.token == OBJECT) {
|
||||
ts += makePackageObject(start, objectDef(in.offset, NoMods))
|
||||
ts ++= joinComment(List(makePackageObject(start, objectDef(in.offset, NoMods))))
|
||||
if (in.token != EOF) {
|
||||
acceptStatSep()
|
||||
ts ++= topStatSeq()
|
||||
}
|
||||
} else {
|
||||
in.flushDoc
|
||||
val pkg = qualId()
|
||||
newLineOptWhenFollowedBy(LBRACE)
|
||||
if (in.token == EOF) {
|
||||
|
|
|
@ -26,6 +26,10 @@ class Settings(error: String => Unit) extends scala.tools.nsc.Settings(error) {
|
|||
* documented. 'Note:'' This setting is currently not used. */
|
||||
val docversion = StringSetting ("-doc-version", "doc-version", "An optional version number, to be appended to the title", "")
|
||||
|
||||
/** A setting that defines a URL to be concatenated with source locations and show a link to source files.
|
||||
* If needed the sourcepath option can be used to exclude undesired initial part of the link to sources */
|
||||
val docsourceurl = StringSetting ("-doc-source-url", "url", "The URL prefix where documentation will link to sources", "")
|
||||
|
||||
// working around issue described in r18708.
|
||||
suppressVTWarn.value = true
|
||||
|
||||
|
|
|
@ -170,6 +170,15 @@ class Template(tpl: DocTemplateEntity) extends HtmlPage {
|
|||
attributes: { fvs map { fv => { inlineToHtml(fv.text) ++ xml.Text(" ") } } }
|
||||
</div>
|
||||
}
|
||||
{ tpl.companion match {
|
||||
case Some(companion) if isSelf =>
|
||||
<div class="block">
|
||||
Go to: <a href={relativeLinkTo(companion)}>companion</a>
|
||||
</div>
|
||||
case _ =>
|
||||
NodeSeq.Empty
|
||||
}
|
||||
}
|
||||
{ val inDefTpls = mbr.inDefinitionTemplates
|
||||
if (inDefTpls.tail.isEmpty && (inDefTpls.head == mbr.inTemplate)) NodeSeq.Empty else {
|
||||
<div class="block">
|
||||
|
@ -178,29 +187,29 @@ class Template(tpl: DocTemplateEntity) extends HtmlPage {
|
|||
}
|
||||
}
|
||||
{ mbr match {
|
||||
case dtpl: DocTemplateEntity if isSelf =>
|
||||
val subClss = dtpl.subClasses
|
||||
if (subClss.isEmpty) NodeSeq.Empty else
|
||||
case dtpl: DocTemplateEntity if (isSelf && !dtpl.subClasses.isEmpty) =>
|
||||
<div class="block">
|
||||
known subclasses: { templatesToHtml(dtpl.subClasses, xml.Text(", ")) }
|
||||
</div>
|
||||
case _ => NodeSeq.Empty
|
||||
}
|
||||
}
|
||||
{ mbr match {
|
||||
case dtpl: DocTemplateEntity if (isSelf && dtpl.sourceUrl.isDefined) =>
|
||||
val sourceUrl = tpl.sourceUrl.get
|
||||
<div class="block">
|
||||
source: { <a href={ sourceUrl.toString }>{ Text(new java.io.File(sourceUrl.getPath).getName) }</a> }
|
||||
</div>
|
||||
case _ => NodeSeq.Empty
|
||||
}
|
||||
}
|
||||
{ if(mbr.deprecation.isEmpty) NodeSeq.Empty else
|
||||
<div class="block"><ol>deprecated:
|
||||
{ <li>{ bodyToHtml(mbr.deprecation.get) }</li> }
|
||||
</ol></div>
|
||||
}
|
||||
{ for(comment <- mbr.comment.toList) yield {
|
||||
<xml:group>
|
||||
{ if(!comment.deprecated.isEmpty)
|
||||
<div class="block"><ol>deprecated:
|
||||
{ for(body <- comment.deprecated.toList) yield <li>{bodyToHtml(body)}</li> }
|
||||
</ol></div>
|
||||
else NodeSeq.Empty
|
||||
}
|
||||
{ if(mbr.isDeprecated)
|
||||
<div class="block"><ol>deprecated:
|
||||
{ for(str <- mbr.deprecationMessage.toList) yield <li>{str}</li> }
|
||||
</ol></div>
|
||||
else NodeSeq.Empty
|
||||
}
|
||||
{ if(!comment.version.isEmpty)
|
||||
<div class="block"><ol>version
|
||||
{ for(body <- comment.version.toList) yield <li>{bodyToHtml(body)}</li> }
|
||||
|
@ -231,15 +240,6 @@ class Template(tpl: DocTemplateEntity) extends HtmlPage {
|
|||
}
|
||||
</xml:group>
|
||||
}}
|
||||
{ tpl.companion match {
|
||||
case Some(companion) if isSelf =>
|
||||
<div class="block">
|
||||
Go to: <a href={relativeLinkTo(companion)}>companion</a>
|
||||
</div>
|
||||
case _ =>
|
||||
NodeSeq.Empty
|
||||
}
|
||||
}
|
||||
</xml:group>
|
||||
|
||||
def kindToString(mbr: MemberEntity): String = mbr match {
|
||||
|
@ -260,12 +260,11 @@ class Template(tpl: DocTemplateEntity) extends HtmlPage {
|
|||
|
||||
/** name, tparams, params, result */
|
||||
def signature(mbr: MemberEntity, isSelf: Boolean): NodeSeq = {
|
||||
val isDeprecated = mbr.isDeprecated || (!mbr.comment.isEmpty && !mbr.comment.get.deprecated.isEmpty)
|
||||
def inside(hasLinks: Boolean): NodeSeq =
|
||||
<xml:group>
|
||||
<span class="kind">{ kindToString(mbr) }</span>
|
||||
<span class="symbol">
|
||||
<span class={"name" + (if(isDeprecated) " deprecated" else "") }>{ if (mbr.isConstructor) tpl.name else mbr.name }</span>{
|
||||
<span class={"name" + (if (mbr.deprecation.isDefined) " deprecated" else "") }>{ if (mbr.isConstructor) tpl.name else mbr.name }</span>{
|
||||
def tparamsToHtml(tpss: List[TypeParam]): NodeSeq =
|
||||
if (tpss.isEmpty) NodeSeq.Empty else {
|
||||
def tparam0(tp: TypeParam): NodeSeq =
|
||||
|
|
|
@ -43,9 +43,8 @@ trait MemberEntity extends Entity {
|
|||
def definitionName: String
|
||||
def visibility: Option[Paragraph]
|
||||
def flags: List[Paragraph]
|
||||
def deprecation: Option[Body]
|
||||
def inheritedFrom: List[TemplateEntity]
|
||||
def isDeprecated: Boolean
|
||||
def deprecationMessage: Option[String]
|
||||
def resultType: TypeEntity
|
||||
def isDef: Boolean
|
||||
def isVal: Boolean
|
||||
|
@ -61,6 +60,7 @@ trait MemberEntity extends Entity {
|
|||
trait DocTemplateEntity extends TemplateEntity with MemberEntity {
|
||||
def toRoot: List[DocTemplateEntity]
|
||||
def inSource: Option[(io.AbstractFile, Int)]
|
||||
def sourceUrl: Option[java.net.URL]
|
||||
def typeParams: List[TypeParam]
|
||||
def parentType: Option[TypeEntity]
|
||||
def linearization: List[TemplateEntity]
|
||||
|
|
|
@ -25,7 +25,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { extractor =
|
|||
|
||||
object commentator {
|
||||
|
||||
private val factory = new CommentFactory(reporter)
|
||||
val factory = new CommentFactory(reporter)
|
||||
|
||||
private val commentCache = mutable.HashMap.empty[(Symbol, TemplateImpl), Comment]
|
||||
|
||||
|
@ -94,8 +94,6 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { extractor =
|
|||
def inDefinitionTemplates =
|
||||
if (inTpl == null)
|
||||
makePackage(RootPackage, null).toList
|
||||
else if (sym.owner == inTpl.sym)
|
||||
inTpl :: Nil
|
||||
else
|
||||
makeTemplate(sym.owner) :: (sym.allOverriddenSymbols map { inhSym => makeTemplate(inhSym.owner) })
|
||||
def visibility = {
|
||||
|
@ -121,11 +119,18 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { extractor =
|
|||
if (!sym.isModule && (sym hasFlag Flags.FINAL)) fgs += Paragraph(Text("final"))
|
||||
fgs.toList
|
||||
}
|
||||
def deprecation =
|
||||
if (sym.isDeprecated && sym.deprecationMessage.isDefined)
|
||||
Some(commentator.factory.parseWiki(sym.deprecationMessage.get, NoPosition))
|
||||
else if (sym.isDeprecated)
|
||||
Some(Body(Nil))
|
||||
else if (comment.isDefined)
|
||||
comment.get.deprecated
|
||||
else
|
||||
None
|
||||
def inheritedFrom =
|
||||
if (inTemplate.sym == this.sym.owner || inTemplate.sym.isPackage) Nil else
|
||||
makeTemplate(this.sym.owner) :: (sym.allOverriddenSymbols map { os => makeTemplate(os.owner) })
|
||||
def isDeprecated = sym.isDeprecated
|
||||
def deprecationMessage = sym.deprecationMessage
|
||||
def resultType = makeType(sym.tpe.finalResultType, inTemplate, sym)
|
||||
def isDef = false
|
||||
def isVal = false
|
||||
|
@ -150,6 +155,18 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { extractor =
|
|||
override def definitionName = optimize(inDefinitionTemplates.head.qualifiedName + "." + name)
|
||||
override def toRoot: List[DocTemplateImpl] = this :: inTpl.toRoot
|
||||
def inSource = if (sym.sourceFile != null) Some(sym.sourceFile, sym.pos.line) else None
|
||||
def sourceUrl = {
|
||||
def fixPath(s: String) = s.replaceAll(java.io.File.separator, "/")
|
||||
val assumedSourceRoot: String = {
|
||||
val fixed = fixPath(settings.sourcepath.value)
|
||||
if (fixed endsWith "/") fixed.dropRight(1) else fixed
|
||||
}
|
||||
if (!settings.docsourceurl.isDefault)
|
||||
inSource map { case (file, _) =>
|
||||
new java.net.URL(settings.docsourceurl.value + "/" + fixPath(file.path).replaceFirst("^" + assumedSourceRoot, ""))
|
||||
}
|
||||
else None
|
||||
}
|
||||
def typeParams = if (sym.isClass) sym.typeParams map (makeTypeParam(_, this)) else Nil
|
||||
def parentType =
|
||||
if (sym.isPackage) None else
|
||||
|
@ -169,7 +186,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { extractor =
|
|||
subClassesCache += sc
|
||||
}
|
||||
def subClasses = subClassesCache.toList
|
||||
protected def memberSyms =
|
||||
protected lazy val memberSyms =
|
||||
// Only this class's constructors are part of its members, inherited constructors are not.
|
||||
sym.info.nonPrivateMembers.filter(x => (!x.isConstructor || x.owner==sym))
|
||||
val members = memberSyms flatMap (makeMember(_, this))
|
||||
|
@ -231,7 +248,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { extractor =
|
|||
override def qualifiedName = "_root_"
|
||||
override def inheritedFrom = Nil
|
||||
override def isRootPackage = true
|
||||
override protected def memberSyms =
|
||||
override protected lazy val memberSyms =
|
||||
(bSym.info.members ++ EmptyPackage.info.members) filter { s =>
|
||||
s != EmptyPackage && s != RootPackage
|
||||
}
|
||||
|
@ -358,13 +375,13 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { extractor =
|
|||
})
|
||||
else if (bSym.isPackage)
|
||||
inTpl match { case inPkg: PackageImpl => makePackage(bSym, inPkg) }
|
||||
else if ((bSym.isClass || bSym.isModule) && (bSym.sourceFile != null) && bSym.isPublic && !bSym.isLocal) {
|
||||
else if ((bSym.isClass || bSym.isModule) && bSym.isPublic && !bSym.isLocal) {
|
||||
(inTpl.toRoot find (_.sym == bSym )) orElse Some(makeDocTemplate(bSym, inTpl))
|
||||
}
|
||||
else
|
||||
None
|
||||
}
|
||||
if (!aSym.isPublic || (aSym hasFlag Flags.SYNTHETIC) || (aSym hasFlag Flags.BRIDGE) || aSym.isLocal || aSym.isModuleClass || aSym.isPackageObject || aSym.isMixinConstructor)
|
||||
if ((!aSym.isPackage && aSym.sourceFile == null) || !aSym.isPublic || (aSym hasFlag Flags.SYNTHETIC) || aSym.isLocal || aSym.isModuleClass || aSym.isPackageObject || aSym.isMixinConstructor)
|
||||
Nil
|
||||
else {
|
||||
val allSyms = useCases(aSym, inTpl.sym) map { case (bSym, bComment, bPos) =>
|
||||
|
|
|
@ -29,10 +29,10 @@ final class CommentFactory(val reporter: Reporter) { parser =>
|
|||
throw FatalError("program logic: " + msg)
|
||||
|
||||
protected val CleanHtml =
|
||||
new Regex("""</?(p|h\d|pre|dl|dt|dd|ol|ul|li|blockquote|div|hr|br|br)\s*/?>""")
|
||||
new Regex("""</?(p|h\d|pre|dl|dt|dd|ol|ul|li|blockquote|div|hr|br|br).*/?>""")
|
||||
|
||||
protected val ShortLineEnd =
|
||||
new Regex("""\.|</(p|h\d|pre|dd|li|div|blockquote)>|<(hr|table)\s*/?>""")
|
||||
new Regex("""\.|</?.*>""")
|
||||
|
||||
/** The body of a comment, dropping start and end markers. */
|
||||
protected val CleanComment =
|
||||
|
@ -220,7 +220,7 @@ final class CommentFactory(val reporter: Reporter) { parser =>
|
|||
* * 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. */
|
||||
protected def parseWiki(string: String, pos: Position): Body =
|
||||
def parseWiki(string: String, pos: Position): Body =
|
||||
new WikiParser(string.toArray, pos).document()
|
||||
|
||||
/** TODO
|
||||
|
|
Loading…
Reference in New Issue