diff options
author | Felix Mulder <felix.mulder@gmail.com> | 2016-11-02 11:08:28 +0100 |
---|---|---|
committer | Guillaume Martres <smarter@ubuntu.com> | 2016-11-22 01:35:07 +0100 |
commit | 8a61ff432543a29234193cd1f7c14abd3f3d31a0 (patch) | |
tree | a8147561d307af862c295cfc8100d271063bb0dd /compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala | |
parent | 6a455fe6da5ff9c741d91279a2dc6fe2fb1b472f (diff) | |
download | dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.gz dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.bz2 dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.zip |
Move compiler and compiler tests to compiler dir
Diffstat (limited to 'compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala')
-rw-r--r-- | compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala b/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala new file mode 100644 index 000000000..24d583b19 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala @@ -0,0 +1,145 @@ +package dotty.tools +package dotc +package reporting + +import core.Contexts.Context +import core.Decorators._ +import printing.Highlighting.{Blue, Red} +import diagnostic.{Message, MessageContainer, NoExplanation} +import diagnostic.messages._ +import util.SourcePosition + +import scala.collection.mutable + +trait MessageRendering { + /** Remove ANSI coloring from `str`, useful for getting real length of + * strings + * + * @return string stripped of ANSI escape codes + */ + def stripColor(str: String): String = + str.replaceAll("\u001B\\[[;\\d]*m", "") + + /** When inlining a method call, if there's an error we'd like to get the + * outer context and the `pos` at which the call was inlined. + * + * @return a list of strings with inline locations + */ + def outer(pos: SourcePosition, prefix: String)(implicit ctx: Context): List[String] = + if (pos.outer.exists) { + s"$prefix| This location is in code that was inlined at ${pos.outer}" :: + outer(pos.outer, prefix) + } else Nil + + /** Get the sourcelines before and after the position, as well as the offset + * for rendering line numbers + * + * @return (lines before error, lines after error, line numbers offset) + */ + def sourceLines(pos: SourcePosition)(implicit ctx: Context): (List[String], List[String], Int) = { + var maxLen = Int.MinValue + def render(xs: List[Int]) = + xs.map(pos.source.offsetToLine(_)) + .map { lineNbr => + val prefix = s"${lineNbr + 1} |" + maxLen = math.max(maxLen, prefix.length) + (prefix, pos.lineContent(lineNbr).stripLineEnd) + } + .map { case (prefix, line) => + val lnum = Red(" " * math.max(0, maxLen - prefix.length) + prefix) + hl"$lnum$line" + } + + val (before, after) = pos.beforeAndAfterPoint + (render(before), render(after), maxLen) + } + + /** The column markers aligned under the error */ + def columnMarker(pos: SourcePosition, offset: Int)(implicit ctx: Context): String = { + val prefix = " " * (offset - 1) + val whitespace = " " * pos.startColumn + val carets = Red { + if (pos.startLine == pos.endLine) + "^" * math.max(1, pos.endColumn - pos.startColumn) + else "^" + } + + s"$prefix|$whitespace${carets.show}" + } + + /** The error message (`msg`) aligned under `pos` + * + * @return aligned error message + */ + def errorMsg(pos: SourcePosition, msg: String, offset: Int)(implicit ctx: Context): String = { + val leastWhitespace = msg.lines.foldLeft(Int.MaxValue) { (minPad, line) => + val lineLength = stripColor(line).length + val currPad = math.min( + math.max(0, ctx.settings.pageWidth.value - offset - lineLength), + offset + pos.startColumn + ) + + math.min(currPad, minPad) + } + + msg.lines + .map { line => " " * (offset - 1) + "|" + (" " * (leastWhitespace - offset)) + line} + .mkString(sys.props("line.separator")) + } + + /** The separator between errors containing the source file and error type + * + * @return separator containing error location and kind + */ + def posStr(pos: SourcePosition, diagnosticLevel: String, message: Message)(implicit ctx: Context): String = + if (pos.exists) Blue({ + val file = pos.source.file.toString + val errId = + if (message.errorId != NoExplanation.ID) + s"[E${"0" * (3 - message.errorId.toString.length) + message.errorId}] " + else "" + val kind = + if (message.kind == "") diagnosticLevel + else s"${message.kind} $diagnosticLevel" + val prefix = s"-- ${errId}${kind}: $file " + + prefix + + ("-" * math.max(ctx.settings.pageWidth.value - stripColor(prefix).length, 0)) + }).show else "" + + /** Explanation rendered under "Explanation" header */ + def explanation(m: Message)(implicit ctx: Context): String = { + val sb = new StringBuilder( + hl"""| + |${Blue("Explanation")} + |${Blue("===========")}""" + ) + sb.append('\n').append(m.explanation) + if (m.explanation.lastOption != Some('\n')) sb.append('\n') + sb.toString + } + + /** The whole message rendered from `msg` */ + def messageAndPos(msg: Message, pos: SourcePosition, diagnosticLevel: String)(implicit ctx: Context): String = { + val sb = mutable.StringBuilder.newBuilder + sb.append(posStr(pos, diagnosticLevel, msg)).append('\n') + if (pos.exists) { + val (srcBefore, srcAfter, offset) = sourceLines(pos) + val marker = columnMarker(pos, offset) + val err = errorMsg(pos, msg.msg, offset) + sb.append((srcBefore ::: marker :: err :: outer(pos, " " * (offset - 1)) ::: srcAfter).mkString("\n")) + } else sb.append(msg.msg) + sb.toString + } + + def diagnosticLevel(cont: MessageContainer): String = + cont match { + case m: Error => "Error" + case m: FeatureWarning => "Feature Warning" + case m: DeprecationWarning => "Deprecation Warning" + case m: UncheckedWarning => "Unchecked Warning" + case m: MigrationWarning => "Migration Warning" + case m: Warning => "Warning" + case m: Info => "Info" + } +} |