Permalink
Browse files

More dry refactoring.

  • Loading branch information...
som-snytt committed May 31, 2018
1 parent e3b1e7c commit 043c5ad21150ff0f1c72053d5c2cf91fa6adf081
@@ -10,71 +10,32 @@ import scala.collection.mutable
import scala.tools.nsc.Settings
import scala.reflect.internal.util.Position
/** This reporter implements filtering by severity and position.
/** This reporter uses filtering by severity and position,
* including two-tiered INFO messaging,
* and also handles prompting and debug output.
*/
abstract class AbstractReporter extends Reporter {
@deprecated("Use PositionFiltering", since="2.13")
abstract class AbstractReporter extends Reporter with PositionFiltering {
val settings: Settings
def display(pos: Position, msg: String, severity: Severity): Unit
def displayPrompt(): Unit
private val positions = mutable.Map[Position, Severity]() withDefaultValue INFO
private val messages = mutable.Map[Position, List[String]]() withDefaultValue Nil
protected def noWarnings = settings.nowarnings.value
private def isVerbose = settings.verbose.value
private def isPromptSet = settings.prompt.value
private def isDebug = settings.debug.value
override def reset(): Unit = {
super.reset()
positions.clear()
messages.clear()
}
private def isVerbose = settings.verbose.value
private def noWarnings = settings.nowarnings.value
private def isPromptSet = settings.prompt.value
private def isDebug = settings.debug
protected def suppressed(pos: Position, msg: String, severity: Severity): Unit =
if (isPromptSet) countAndDisplay(pos, msg, severity)
else if (isDebug) countAndDisplay(pos, "[ suppressed ] " + msg, severity)
protected def info0(pos: Position, msg: String, severity: Severity, force: Boolean): Unit = {
if (severity == INFO) {
if (isVerbose || force) {
severity.count += 1
display(pos, msg, severity)
}
}
else {
val hidden = testAndLog(pos, severity, msg)
if (severity == WARNING && noWarnings) ()
else {
if (!hidden || isPromptSet) {
severity.count += 1
display(pos, msg, severity)
}
else if (isDebug) {
severity.count += 1
display(pos, "[ suppressed ] " + msg, severity)
}
protected def info0(pos: Position, msg: String, severity: Severity, force: Boolean): Unit =
if (filter(pos, msg, severity) && (severity != INFO || (force || isVerbose)))
countAndDisplay(pos, msg, severity)
if (isPromptSet)
displayPrompt()
}
}
private def countAndDisplay(pos: Position, msg: String, severity: Severity): Unit = {
severity.count += 1
display(pos, msg, severity)
if (isPromptSet && severity != INFO) displayPrompt()
}
/** Logs a position and returns true if it was already logged.
* @note Two positions are considered identical for logging if they have the same point.
*/
private def testAndLog(pos: Position, severity: Severity, msg: String): Boolean =
pos != null && pos.isDefined && {
val fpos = pos.focus
val suppress = positions(fpos) match {
case ERROR => true // already error at position
case highest
if highest.id > severity.id => true // already message higher than present severity
case `severity` => messages(fpos) contains msg // already issued this exact message
case _ => false // good to go
}
suppress || {
positions(fpos) = severity
messages(fpos) ::= msg
false
}
}
}
@@ -15,81 +15,18 @@ import scala.tools.util.SystemExit
/** This class implements a Reporter that displays messages on a text console.
*/
class ConsoleReporter(val settings: Settings, reader: BufferedReader, writer: PrintWriter, echoWriter: PrintWriter) extends AbstractReporter {
class ConsoleReporter(val settings: Settings, val reader: BufferedReader, val writer: PrintWriter, val echoWriter: PrintWriter) extends AbstractReporter with PrintReporter with SummaryReporter {
def this(settings: Settings) = this(settings, Console.in, new PrintWriter(Console.err, true), new PrintWriter(Console.out, true))
def this(settings: Settings, reader: BufferedReader, writer: PrintWriter) = this(settings, reader, writer, writer)
/** Whether a short file name should be displayed before errors */
var shortname: Boolean = false
/** maximal number of error messages to be printed */
@deprecated("configured by settings.maxerrs", since="2.12.2")
final val ERROR_LIMIT = 100
private def label(severity: Severity): String = severity match {
case ERROR => "error"
case WARNING => "warning"
case INFO => ""
}
protected def clabel(severity: Severity): String = label(severity) match {
case "" => ""
case s => s"$s: "
}
/** Prints the message. */
def printMessage(msg: String): Unit = {
writer.println(trimTrailing(msg))
writer.flush()
}
/** Prints the message to the echoWriter, which is usually stdout. */
override def echo(msg: String): Unit = {
echoWriter.println(trimTrailing(msg))
echoWriter.flush()
}
/** Prints the message with the given position indication. */
def printMessage(posIn: Position, msg: String): Unit = printMessage(formatMessage(posIn, msg, shortname))
def print(pos: Position, msg: String, severity: Severity): Unit = printMessage(pos, s"${clabel(severity)}${msg}")
/** Prints the column marker of the given position. */
def printColumnMarker(pos: Position): Unit = if (pos.isDefined) printMessage(" " * (pos.column - 1) + "^")
/** Prints the number of warnings and errors if there are any. */
def printSummary(): Unit =
for (k <- List(WARNING, ERROR) if k.count > 0) printMessage(s"${countAs(k.count, label(k))} found")
def display(pos: Position, msg: String, severity: Severity): Unit = {
// limit errors and warnings; AbstractReporter counts them because of forced INFO special case
override def display(pos: Position, msg: String, severity: Severity): Unit = {
// the count includes the current message
val ok = severity match {
case ERROR => ERROR.count <= settings.maxerrs.value
case WARNING => WARNING.count <= settings.maxwarns.value
case _ => true
case _ => true
}
if (ok) print(pos, msg, severity)
if (ok) super.display(pos, msg, severity)
}
def displayPrompt(): Unit = {
writer.println()
writer.print("a)bort, s)tack, r)esume: ")
writer.flush()
if (reader != null) {
reader.read match {
case 'a' | 'A' =>
new Throwable().printStackTrace(writer)
throw SystemExit(1)
case 's' | 'S' =>
new Throwable().printStackTrace(writer)
writer.println()
writer.flush()
case _ =>
}
}
}
override def flush() = writer.flush()
override def finish() = printSummary()
}
@@ -14,46 +14,44 @@ import scala.reflect.internal.util.{Position, StringOps}
import Position.formatMessage
import StringOps.{countElementsAsString => countAs, trimAllTrailingSpace => trimTrailing}
/** This class implements a Reporter that displays messages on a text console.
*/
class DisplayReporter(settings: Settings, reader: BufferedReader, writer: PrintWriter, echoWriter: PrintWriter) extends InternalReporter {
/** Facility for outputting messages, with optional user intervention. */
trait PrintReporter extends InternalReporter {
def settings: Settings
def reader: BufferedReader
def writer: PrintWriter
def echoWriter: PrintWriter
/** Whether a short file name should be displayed before errors */
var shortname: Boolean = false
private def label(severity: Severity): String = severity match {
case ERROR => "error"
case WARNING => "warning"
case _ => ""
}
private def clabel(severity: Severity): String = severity match {
case ERROR => "error: "
case WARNING => "warning: "
case _ => ""
}
/** Prints the message. */
def printMessage(msg: String): Unit = {
/** Prints the warning or error message. */
private def printMessage(msg: String): Unit = {
writer.println(trimTrailing(msg))
writer.flush()
if (settings.prompt) displayPrompt()
}
/** Prints the message to the echoWriter, which is usually stdout. */
override def echo(msg: String): Unit = {
private def echoMessage(msg: String): Unit = {
echoWriter.println(trimTrailing(msg))
echoWriter.flush()
}
/** Prints the message with the given position indication. */
def printMessage(posIn: Position, msg: String): Unit = printMessage(formatMessage(posIn, msg, shortname))
def print(pos: Position, msg: String, severity: Severity): Unit = printMessage(pos, s"${clabel(severity)}${msg}")
/** Prints the column marker of the given position. */
def printColumnMarker(pos: Position): Unit = if (pos.isDefined) printMessage(" " * (pos.column - 1) + "^")
def info0(pos: Position, msg: String, severity: Severity, force: Boolean): Unit = print(pos, msg, severity)
/** Format a message and emit it. */
def display(pos: Position, msg: String, severity: Severity): Unit = {
val text = formatMessage(pos, s"${clabel(severity)}${msg}", shortname)
severity match {
case INFO => echoMessage(text)
case _ => printMessage(text)
}
}
def displayPrompt(): Unit = {
writer.println()
@@ -76,14 +74,22 @@ class DisplayReporter(settings: Settings, reader: BufferedReader, writer: PrintW
override def flush() = {
writer.flush()
if (writer ne echoWriter) echoWriter.flush()
super.flush()
}
override def finish() = {
def close() = {
writer.close()
if (writer ne echoWriter) echoWriter.close()
}
}
/** This class implements a Reporter that displays messages on a text console.
*/
class DisplayReporter(val settings: Settings, val reader: BufferedReader, val writer: PrintWriter, val echoWriter: PrintWriter) extends InternalReporter with PrintReporter {
def info0(pos: Position, msg: String, severity: Severity, force: Boolean): Unit = display(pos, msg, severity)
}
object DisplayReporter {
def apply(
settings: Settings = new Settings,
@@ -97,46 +103,59 @@ object DisplayReporter {
class DefaultReporter(settings: Settings, writer: PrintWriter, echo: PrintWriter)
extends PositionFilter(settings, DisplayReporter(settings, Console.in, writer, echo))
with CountingReporter
with SummaryReporter
with LimitFilter {
// required constructor for -Xreporter
def this(settings: Settings) = this(settings, new PrintWriter(Console.err, true), new PrintWriter(Console.out, true))
def shortname_=(flag: Boolean): Unit = delegate.asInstanceOf[DisplayReporter].shortname = flag
def shortname: Boolean = delegate.asInstanceOf[DisplayReporter].shortname
private def displayReporter = delegate.asInstanceOf[DisplayReporter]
def shortname_=(flag: Boolean): Unit = displayReporter.shortname = flag
def shortname: Boolean = displayReporter.shortname
def maxerrs = settings.maxerrs.value
def maxwarns = settings.maxwarns.value
// closes output writers; reporter will go silent
def close(): Unit = displayReporter.close()
}
object DefaultReporter {
def apply(settings: Settings) = new DefaultReporter(settings)
def apply(settings: Settings, out: PrintWriter) = new DefaultReporter(settings, out, out)
}
/** A `Reporter` that echos a summary in `finish`. */
trait SummaryReporter extends InternalReporter {
/** Prints the number of warnings and errors if there are any. */
override def finish(): Unit = {
import reflect.internal.util.StringOps.{countElementsAsString => countAs}
if (hasWarnings) echo(s"${countAs(WARNING.count, WARNING.toString.toLowerCase)} found")
if (hasErrors) echo(s"${countAs(ERROR.count, ERROR.toString.toLowerCase)} found")
super.finish()
}
}
/** Common abstraction for filtering reporter messages. */
trait Filtering { _: InternalReporter =>
/* True to permit the message. */
protected def filter(pos: Position, msg: String, severity: Severity): Boolean
}
/** A `ForwardingReporter` that filters events before delegating.
*
* Concrete subclasses should implement just the abstract `filter` method.
*/
trait FilteringReporter extends ForwardingReporter {
/* True to permit the message. */
protected def filter(pos: Position, msg: String, severity: Severity): Boolean
trait FilteringReporter extends ForwardingReporter with Filtering {
override def echo(pos: Position, msg: String) = if (filter(pos, msg, INFO)) delegate.echo(pos, msg)
override def warning(pos: Position, msg: String) = if (filter(pos, msg, WARNING)) delegate.warning(pos, msg)
override def error(pos: Position, msg: String) = if (filter(pos, msg, ERROR)) delegate.error(pos, msg)
}
/** A `Reporter` that counts messages that are passed by the filter and echos a summary in `finish`. */
/** A `Reporter` that counts messages that are passed by the filter. */
trait CountingReporter extends FilteringReporter {
abstract override protected def filter(pos: Position, msg: String, severity: Severity): Boolean =
super.filter(pos, msg, severity) && { severity.count += 1 ; true }
/** Prints the number of warnings and errors if there are any. */
override def finish(): Unit = {
import reflect.internal.util.StringOps.{countElementsAsString => countAs}
if (hasWarnings) echo(s"${countAs(WARNING.count, WARNING.toString.toLowerCase)} found")
if (hasErrors) echo(s"${countAs(ERROR.count, ERROR.toString.toLowerCase)} found")
super.finish()
}
}
/** Disable a message when super.filter has passed the message but max limit has been reached. */
/** Disable a message when super.filter has passed the message but max limit has been reached.
* `hasErrors` is implemented as a flag to defer initializing ERROR object.
*/
trait LimitFilter extends FilteringReporter {
def maxerrs: Int
def maxwarns: Int
@@ -153,4 +172,10 @@ trait LimitFilter extends FilteringReporter {
case WARNING => warned = true ; WARNING.count <= maxwarns
case _ => true
}}
override def reset(): Unit = {
super.reset()
warned = false
erred = false
}
}
@@ -11,9 +11,14 @@ import scala.tools.nsc.Settings
import scala.reflect.internal.{Reporter => InternalReporter}
import scala.reflect.internal.util.Position
/** This reporter implements filtering by severity and position.
*/
class PositionFilter(settings: Settings, protected val delegate: InternalReporter) extends InternalReporter with FilteringReporter {
/** Filtering by severity and position. */
trait PositionFiltering extends InternalReporter with Filtering {
/** True to disable warnings. */
protected def noWarnings: Boolean
/** Invoked when an error or warning is filtered by position. */
protected def suppressed(pos: Position, msg: String, severity: Severity): Unit
private val positions = mutable.Map[Position, Severity]() withDefaultValue INFO
private val messages = mutable.Map[Position, List[String]]() withDefaultValue Nil
@@ -27,11 +32,8 @@ class PositionFilter(settings: Settings, protected val delegate: InternalReporte
override protected def filter(pos: Position, msg: String, severity: Severity) =
severity match {
case INFO => true
case WARNING if settings.nowarnings => false
case _ =>
val hidden = testAndLog(pos, severity, msg)
if (hidden && settings.debug) forward(pos, "[ suppressed ] " + msg, severity)
!hidden
case WARNING if noWarnings => false
case _ => !testAndLog(pos, severity, msg) || { suppressed(pos, msg, severity) ; false }
}
/** Logs a position and returns true if it was already logged.
@@ -55,3 +57,12 @@ class PositionFilter(settings: Settings, protected val delegate: InternalReporte
}
}
}
/** This reporter implements filtering by severity and position.
*/
class PositionFilter(settings: Settings, protected val delegate: InternalReporter) extends InternalReporter with FilteringReporter with PositionFiltering {
protected def noWarnings = settings.nowarnings
protected def suppressed(pos: Position, msg: String, severity: Severity): Unit =
if (settings.prompt) forward(pos, msg, severity)
else if (settings.debug) forward(pos, "[ suppressed ] " + msg, severity)
}
Oops, something went wrong.

0 comments on commit 043c5ad

Please sign in to comment.