Skip to content

Commit 9e30bee

Browse files
felixmulderlrytz
authored andcommitted
Add summary reporting to Scaladoc (scala#5063)
1 parent 554af4d commit 9e30bee

File tree

7 files changed

+81
-23
lines changed

7 files changed

+81
-23
lines changed

src/scaladoc/scala/tools/ant/Scaladoc.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ import org.apache.tools.ant.Project
1414
import org.apache.tools.ant.types.{Path, Reference}
1515
import org.apache.tools.ant.util.{FileUtils, GlobPatternMapper}
1616

17+
import scala.tools.nsc.ScalaDocReporter
1718
import scala.tools.nsc.doc.Settings
18-
import scala.tools.nsc.reporters.ConsoleReporter
1919

2020
/** An Ant task to document Scala code.
2121
*
@@ -666,7 +666,7 @@ class Scaladoc extends ScalaMatchingTask {
666666
/** Performs the compilation. */
667667
override def execute() = {
668668
val (docSettings, sourceFiles) = initialize
669-
val reporter = new ConsoleReporter(docSettings)
669+
val reporter = new ScalaDocReporter(docSettings)
670670
try {
671671
val docProcessor = new scala.tools.nsc.doc.DocFactory(reporter, docSettings)
672672
docProcessor.document(sourceFiles.map (_.toString))

src/scaladoc/scala/tools/nsc/ScalaDoc.scala

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ package scala.tools.nsc
88

99
import scala.tools.nsc.doc.DocFactory
1010
import scala.tools.nsc.reporters.ConsoleReporter
11-
import scala.reflect.internal.util.FakePos
11+
import scala.reflect.internal.Reporter
12+
import scala.reflect.internal.util.{ FakePos, NoPosition, Position }
1213

1314
/** The main class for scaladoc, a front-end for the Scala compiler
1415
* that generates documentation from source files.
@@ -38,23 +39,43 @@ class ScalaDoc {
3839
reporter.echo(command.usageMsg)
3940
else
4041
try { new DocFactory(reporter, docSettings) document command.files }
41-
catch {
42-
case ex @ FatalError(msg) =>
43-
if (docSettings.debug.value) ex.printStackTrace()
44-
reporter.error(null, "fatal error: " + msg)
45-
}
46-
finally reporter.printSummary()
42+
catch {
43+
case ex @ FatalError(msg) =>
44+
if (docSettings.debug.value) ex.printStackTrace()
45+
reporter.error(null, "fatal error: " + msg)
46+
}
47+
finally reporter.printSummary()
4748

4849
!reporter.reallyHasErrors
4950
}
5051
}
5152

53+
/** The Scaladoc reporter adds summary messages to the `ConsoleReporter`
54+
*
55+
* Use the `summaryX` methods to add unique summarizing message to the end of
56+
* the run.
57+
*/
5258
class ScalaDocReporter(settings: Settings) extends ConsoleReporter(settings) {
59+
import scala.collection.mutable.LinkedHashMap
5360

5461
// need to do sometimes lie so that the Global instance doesn't
5562
// trash all the symbols just because there was an error
5663
override def hasErrors = false
5764
def reallyHasErrors = super.hasErrors
65+
66+
private[this] val delayedMessages: LinkedHashMap[(Position, String), () => Unit] =
67+
LinkedHashMap.empty
68+
69+
/** Eliminates messages if both `pos` and `msg` are equal to existing element */
70+
def addDelayedMessage(pos: Position, msg: String, print: () => Unit): Unit =
71+
delayedMessages += ((pos, msg) -> print)
72+
73+
def printDelayedMessages(): Unit = delayedMessages.values.foreach(_.apply())
74+
75+
override def printSummary(): Unit = {
76+
printDelayedMessages()
77+
super.printSummary()
78+
}
5879
}
5980

6081
object ScalaDoc extends ScalaDoc {
@@ -70,4 +91,20 @@ object ScalaDoc extends ScalaDoc {
7091
def main(args: Array[String]): Unit = sys exit {
7192
if (process(args)) 0 else 1
7293
}
94+
95+
implicit class SummaryReporter(val rep: Reporter) extends AnyVal {
96+
/** Adds print lambda to ScalaDocReporter, executes it on other reporter */
97+
private[this] def summaryMessage(pos: Position, msg: String, print: () => Unit): Unit = rep match {
98+
case r: ScalaDocReporter => r.addDelayedMessage(pos, msg, print)
99+
case _ => print()
100+
}
101+
102+
def summaryEcho(pos: Position, msg: String): Unit = summaryMessage(pos, msg, () => rep.echo(pos, msg))
103+
def summaryError(pos: Position, msg: String): Unit = summaryMessage(pos, msg, () => rep.error(pos, msg))
104+
def summaryWarning(pos: Position, msg: String): Unit = summaryMessage(pos, msg, () => rep.warning(pos, msg))
105+
106+
def summaryEcho(msg: String): Unit = summaryEcho(NoPosition, msg)
107+
def summaryError(msg: String): Unit = summaryError(NoPosition, msg)
108+
def summaryWarning(msg: String): Unit = summaryWarning(NoPosition, msg)
109+
}
73110
}

src/scaladoc/scala/tools/nsc/doc/DocFactory.scala

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
package scala.tools.nsc
77
package doc
88

9-
import scala.util.control.ControlThrowable
109
import reporters.Reporter
10+
import scala.util.control.ControlThrowable
1111
import scala.reflect.internal.util.BatchSourceFile
1212

1313
/** A documentation processor controls the process of generating Scala
@@ -105,7 +105,19 @@ class DocFactory(val reporter: Reporter, val settings: doc.Settings) { processor
105105
def generate() = {
106106
import doclet._
107107
val docletClass = Class.forName(settings.docgenerator.value) // default is html.Doclet
108-
val docletInstance = docletClass.newInstance().asInstanceOf[Generator]
108+
val docletInstance =
109+
docletClass
110+
.getConstructors
111+
.find { constr =>
112+
constr.getParameterTypes.length == 1 &&
113+
constr.getParameterTypes.apply(0) == classOf[scala.reflect.internal.Reporter]
114+
}
115+
.map(_.newInstance(reporter))
116+
.getOrElse{
117+
reporter.warning(null, "Doclets should be created with the Reporter constructor, otherwise logging reporters will not be shared by the creating parent")
118+
docletClass.newInstance()
119+
}
120+
.asInstanceOf[Generator]
109121

110122
docletInstance match {
111123
case universer: Universer =>

src/scaladoc/scala/tools/nsc/doc/html/Doclet.scala

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,19 @@ package scala.tools.nsc
77
package doc
88
package html
99

10+
import scala.reflect.internal.Reporter
1011
import doclet._
1112

1213
/** The default doclet used by the scaladoc command line tool
1314
* when no user-provided doclet is provided. */
14-
class Doclet extends Generator with Universer {
15+
class Doclet(reporter: Reporter) extends Generator with Universer {
1516

16-
def generateImpl() {
17-
new html.HtmlFactory(universe, new ScalaDocReporter(universe.settings)).generate()
18-
}
17+
@deprecated("Doclets should be created with the Reporter constructor. Otherwise logging reporters will not be shared by the creating parent", "2.12.0")
18+
def this() = this(null)
1919

20+
def generateImpl() =
21+
new html.HtmlFactory(
22+
universe,
23+
if (reporter != null) reporter else new ScalaDocReporter(universe.settings)
24+
).generate()
2025
}

src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@ import java.io.{ File => JFile }
1212
import io.{ Streamable, Directory }
1313
import scala.collection._
1414
import page.diagram._
15+
import scala.reflect.internal.Reporter
1516

1617
/** A class that can generate Scaladoc sites to some fixed root folder.
1718
* @author David Bernard
1819
* @author Gilles Dubochet */
19-
class HtmlFactory(val universe: doc.Universe, val reporter: ScalaDocReporter) {
20-
import page.IndexScript
20+
class HtmlFactory(val universe: doc.Universe, val reporter: Reporter) {
21+
import page.{IndexScript, EntityPage}
2122

2223
/** The character encoding to be used for generated Scaladoc sites.
2324
* This value is currently always UTF-8. */

src/scaladoc/scala/tools/nsc/doc/html/HtmlPage.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import base._
1313
import base.comment._
1414
import model._
1515

16+
import scala.reflect.internal.Reporter
1617
import scala.xml.NodeSeq
1718
import scala.xml.Elem
1819
import scala.xml.dtd.DocType
@@ -27,7 +28,7 @@ abstract class HtmlPage extends Page { thisPage =>
2728
protected def title: String
2829

2930
/** ScalaDoc reporter for error handling */
30-
protected def reporter: ScalaDocReporter
31+
protected def docletReporter: Reporter
3132

3233
/** The page description */
3334
protected def description: String =

src/scaladoc/scala/tools/nsc/doc/html/page/Entity.scala

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ package page
1313
import base._
1414
import base.comment._
1515

16+
import scala.reflect.internal.Reporter
1617
import scala.collection.mutable
1718
import scala.xml.{NodeSeq, Text, UnprefixedAttribute}
1819
import scala.language.postfixOps
@@ -22,10 +23,12 @@ import model.diagram._
2223
import diagram._
2324

2425
trait EntityPage extends HtmlPage {
26+
import ScalaDoc.SummaryReporter
27+
2528
def universe: doc.Universe
2629
def generator: DiagramGenerator
2730
def tpl: DocTemplateEntity
28-
def reporter: ScalaDocReporter
31+
def docletReporter: Reporter
2932

3033
override val path = templateToPath(tpl)
3134

@@ -158,8 +161,7 @@ trait EntityPage extends HtmlPage {
158161
val version = universe.settings.docversion.value
159162

160163
if (version.length > "XX.XX.XX-XXX".length) {
161-
reporter.warning(null,
162-
s"doc-version ($version) is too long to be displayed in the webview")
164+
docletReporter.summaryWarning(s"doc-version ($version) was too long to be displayed in the webview, and will be left out. The max length is: XX.XX.XX-XXX")
163165
""
164166
} else version
165167
}
@@ -1124,12 +1126,12 @@ object EntityPage {
11241126
uni: doc.Universe,
11251127
gen: DiagramGenerator,
11261128
docTpl: DocTemplateEntity,
1127-
rep: ScalaDocReporter
1129+
rep: Reporter
11281130
): EntityPage = new EntityPage {
11291131
def universe = uni
11301132
def generator = gen
11311133
def tpl = docTpl
1132-
def reporter = rep
1134+
def docletReporter = rep
11331135
}
11341136

11351137
/* Vlad: Lesson learned the hard way: don't put any stateful code that references the model here,

0 commit comments

Comments
 (0)