From 19414c084cccf2f560b9f7513ccebe10ee1352d9 Mon Sep 17 00:00:00 2001 From: "alexander.nutz" Date: Fri, 29 Mar 2024 12:21:09 +0100 Subject: [PATCH] fancy error printing done --- README.md | 39 ++++++ img.png | Bin 0 -> 21627 bytes src/main/kotlin/blitz/codeerrors/Errors.kt | 125 ++++++++++++++---- src/main/kotlin/blitz/collections/Bounds.kt | 17 +++ .../blitz/collections/MergeNeighbors.kt | 22 +++ src/main/kotlin/blitz/str/ColoredChar.kt | 21 +++ .../str/MutMultiColoredMultiLineString.kt | 109 +++++++++++++++ .../kotlin/blitz/str/MutMultiColoredString.kt | 92 +++++++++++++ .../kotlin/blitz/str/MutMultiLineString.kt | 6 + src/main/kotlin/blitz/str/MutString.kt | 14 +- src/main/kotlin/blitz/term/AnsiiStr.kt | 8 +- src/main/kotlin/blitz/term/Terminal.kt | 17 +++ 12 files changed, 439 insertions(+), 31 deletions(-) create mode 100644 img.png create mode 100644 src/main/kotlin/blitz/collections/Bounds.kt create mode 100644 src/main/kotlin/blitz/collections/MergeNeighbors.kt create mode 100644 src/main/kotlin/blitz/str/ColoredChar.kt create mode 100644 src/main/kotlin/blitz/str/MutMultiColoredMultiLineString.kt create mode 100644 src/main/kotlin/blitz/str/MutMultiColoredString.kt diff --git a/README.md b/README.md index 4a68902..603d96b 100644 --- a/README.md +++ b/README.md @@ -98,5 +98,44 @@ val b = arrayOf(1, 2, 3, 4) println(a.contents == b.contents) // true println(b.contents) // [1, 2, 3, 4] ``` +### Code error messages +````kotlin +val source = Errors.Source("main.kt", MutMultiLineString.from(""" + fn main() { + return 1 + 0 + } +""".trimIndent(), ' ')) + +val errors = listOf( + Errors.Error( + "cannot return integer from function with return type void", + Errors.Error.Level.ERROR, + Errors.Location(source, 1, 11, 5) + ), + Errors.Error( + "return is deprecated. use yeet instead", + Errors.Error.Level.WARN, + Errors.Location(source, 1, 4, 6) + ), + Errors.Error( + "useless addition", + Errors.Error.Level.INFO, + Errors.Location(source, 1, 13, 3), + isHint = true + ), + Errors.Error( + "Visit https://www.example.com/doc/yeet for more information", + Errors.Error.Level.INFO, + Errors.Location(source, 1, 0, 0), + isLongDesc = true + ) +) + +val config = Errors.PrintConfig() + +Errors.print(config, errors) +```` +Output: +![img.png](img.png) ### Either No example yet diff --git a/img.png b/img.png new file mode 100644 index 0000000000000000000000000000000000000000..96c3905569e466568e2e41d8cdcf9515e6a5529e GIT binary patch literal 21627 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 literal 0 HcmV?d00001 diff --git a/src/main/kotlin/blitz/codeerrors/Errors.kt b/src/main/kotlin/blitz/codeerrors/Errors.kt index 083e520..8a9aae8 100644 --- a/src/main/kotlin/blitz/codeerrors/Errors.kt +++ b/src/main/kotlin/blitz/codeerrors/Errors.kt @@ -1,5 +1,8 @@ package blitz.codeerrors +import blitz.collections.inBounds +import blitz.str.ColoredChar +import blitz.str.MutMultiColoredMultiLineString import blitz.str.MutMultiLineString import blitz.str.MutString import blitz.term.AnsiiMode @@ -23,6 +26,7 @@ object Errors { val level: Level, val loc: Location, val isHint: Boolean = false, + val isLongDesc: Boolean = false, ) { enum class Level { INFO, @@ -41,19 +45,19 @@ object Errors { data class PrintConfig( val styles: Map = mapOf( - Error.Level.INFO to Terminal.COLORS.WHITE.fg, + Error.Level.INFO to Terminal.COLORS.MAGENTA.fg, Error.Level.WARN to Terminal.COLORS.YELLOW.fg, Error.Level.ERROR to Terminal.COLORS.RED.fg ), val levelStr: (Error.Level) -> String = { when (it) { - Error.Level.INFO -> "note" - Error.Level.WARN -> "warning" - Error.Level.ERROR -> "error" + Error.Level.INFO -> "Note" + Error.Level.WARN -> "Warning" + Error.Level.ERROR -> "Error" } }, val underlineString: (len: Int) -> String = { - if (it == 0) "" else "^" + "~".repeat(it - 1) + if (it == 0) "" else "^" + "^".repeat(it - 1) }, ) @@ -63,40 +67,90 @@ object Errors { val worst = errors .map { it.level } .reduce { acc, level -> acc + level } - Terminal.errln("File: \"${source.file}\"", config.styles[worst]!!) - Terminal.errln("================================================================================", config.styles[worst]!!) + Terminal.errln("File: \"${source.file}\"", config.styles[worst]!! + Terminal.STYLES.BOLD) + Terminal.errln("================================================================================", config.styles[worst]!! + Terminal.STYLES.BOLD) - val perLines = errors + val perLinesMap = errors .groupBy { it.loc.line } + + val perLines = perLinesMap .entries .sortedBy { it.key } - perLines.forEach { (line, errors) -> - errors.asSequence().filterNot { it.isHint }.forEach { err -> + perLines.forEachIndexed { index, (line, errors) -> + if (index > 0) + Terminal.errln("") + + errors.asSequence().filterNot { it.isHint || it.isLongDesc }.forEach { err -> Terminal.err(config.levelStr(err.level), config.styles[err.level]!!, Terminal.STYLES.BOLD) - Terminal.errln(": ${err.message}", Terminal.STYLES.BOLD) + Terminal.errln(": ${err.message}", Terminal.COLORS.WHITE.brighter.fg, Terminal.STYLES.BOLD) } - val msg = MutMultiLineString(' ') - val lineStr = line.toString() - msg[1, 2] = lineStr - var nextCol = 3 + lineStr.length - msg[0, nextCol] = '|' // TODO: print above and below source but dimmed? - msg[1, nextCol] = '|' - msg[2, nextCol] = '|' + val printPrev = line > 0 && !perLinesMap.containsKey(line - 1) + val printNext = source.content.lines.inBounds(line + 1) && !perLinesMap.containsKey(line + 1) + + val worstLine = config.styles[ + errors + .map { it.level } + .reduce { acc, level -> acc + level } + ]!! + + val msg = MutMultiColoredMultiLineString(fill = ColoredChar(' ')) + + val lineStr = (line + 1).toString() + msg.set(1, 2, lineStr, worstLine) + + var nextCol = if (printNext) { + val nextLineStr = (line + 2).toString() + msg.set(2, 2, nextLineStr, Terminal.COLORS.WHITE.fg) + 3 + nextLineStr.length + } else { + 3 + lineStr.length + } + + if (printPrev) + msg.set(0, 2, line.toString(), Terminal.COLORS.WHITE.fg) + + fun pipe(row: Int, col: Int) = + msg.set(row, col, '|', Terminal.COLORS.WHITE.fg + Terminal.STYLES.BOLD) + + pipe(0, nextCol) + msg.set(1, nextCol, '|', worstLine + Terminal.STYLES.BOLD) + pipe(2, nextCol) + nextCol += 2 - if (line > 0) - msg[0, nextCol] = source.content[line - 1] - msg[1, nextCol] = source.content[line] + if (printPrev) + msg.set(0, nextCol, source.content[line - 1].toString(), Terminal.COLORS.WHITE.fg) + msg.set(1, nextCol, source.content[line].toString(), Terminal.COLORS.WHITE.brighter.fg) + if (printNext) + msg.set(2, nextCol, source.content[line + 1].toString(), Terminal.COLORS.WHITE.fg) - // TODO: underline + val byCol = errors.asSequence().sortedBy { it.loc.col } + byCol.filterNot { it.isHint || it.isLongDesc }.forEach { + msg.set(2, it.loc.col + nextCol, config.underlineString(it.loc.size), config.styles[it.level]!! + Terminal.STYLES.BOLD) + } - // TODO: hints + var row = 3 + byCol.filter { it.isHint }.forEach { + msg.set(row, it.loc.col + nextCol, config.underlineString(it.loc.size), config.styles[it.level]!! + Terminal.STYLES.BOLD) + val end = it.loc.col + nextCol + it.loc.size + msg.set(row, end + 1, it.message, config.styles[it.level]!!) + pipe(row, nextCol - 2) + row ++ + } + + row ++ + + byCol.filter { it.isLongDesc }.forEach { + val msgLines = MutMultiLineString.from(it.message, fill = ' ') + msg.set(row, nextCol, msgLines, Terminal.COLORS.WHITE.brighter.fg) + row ++ + } Terminal.errln(msg.toString()) } - Terminal.errln("================================================================================", config.styles[worst]!!) + Terminal.errln("================================================================================", config.styles[worst]!! + Terminal.STYLES.BOLD) } } } @@ -104,15 +158,32 @@ object Errors { fun main() { val source = Errors.Source("main.kt", MutMultiLineString.from(""" fn main() { - return 1 + return 1 + 0 } """.trimIndent(), ' ')) val errors = listOf( Errors.Error( - "Cannot return integer from function with return type void", + "cannot return integer from function with return type void", Errors.Error.Level.ERROR, - Errors.Location(source, 1, 11, 1) + Errors.Location(source, 1, 11, 5) + ), + Errors.Error( + "return is deprecated. use yeet instead", + Errors.Error.Level.WARN, + Errors.Location(source, 1, 4, 6) + ), + Errors.Error( + "useless addition", + Errors.Error.Level.INFO, + Errors.Location(source, 1, 13, 3), + isHint = true + ), + Errors.Error( + "Visit https://www.example.com/doc/yeet for more information", + Errors.Error.Level.INFO, + Errors.Location(source, 1, 0, 0), + isLongDesc = true ) ) diff --git a/src/main/kotlin/blitz/collections/Bounds.kt b/src/main/kotlin/blitz/collections/Bounds.kt new file mode 100644 index 0000000..c0954cf --- /dev/null +++ b/src/main/kotlin/blitz/collections/Bounds.kt @@ -0,0 +1,17 @@ +package blitz.collections + +fun List.inBounds(index: Int): Boolean { + if (index >= size) + return false + if (index < 0) + return false + return true +} + +fun Vec.inBounds(index: Int): Boolean { + if (index >= size) + return false + if (index < 0) + return false + return true +} \ No newline at end of file diff --git a/src/main/kotlin/blitz/collections/MergeNeighbors.kt b/src/main/kotlin/blitz/collections/MergeNeighbors.kt new file mode 100644 index 0000000..3eaedf5 --- /dev/null +++ b/src/main/kotlin/blitz/collections/MergeNeighbors.kt @@ -0,0 +1,22 @@ +package blitz.collections + +fun Iterable.mergeNeighbors( + to: MutableList>> = mutableListOf(), + by: (T) -> G +): MutableList>> { + val out = mutableListOf>>() + forEach { + val b = by(it) + if (b == out.lastOrNull()?.first) + out.last().second.add(it) + else + out.add(b to mutableListOf(it)) + } + return out +} + +fun Sequence.mergeNeighbors( + to: MutableList>> = mutableListOf(), + by: (T) -> G +): MutableList>> = + asIterable().mergeNeighbors(to, by) \ No newline at end of file diff --git a/src/main/kotlin/blitz/str/ColoredChar.kt b/src/main/kotlin/blitz/str/ColoredChar.kt new file mode 100644 index 0000000..bf7ce4c --- /dev/null +++ b/src/main/kotlin/blitz/str/ColoredChar.kt @@ -0,0 +1,21 @@ +package blitz.str + +import blitz.term.AnsiiMode +import blitz.term.Terminal + +class ColoredChar( + val char: Char, + val style: AnsiiMode = AnsiiMode(mutableListOf()) +) { + override fun equals(other: Any?): Boolean = + char == other + + override fun hashCode(): Int = + char.hashCode() + + override fun toString(): String = + Terminal.encodeString("$char", style) +} + +fun Iterable.convToString(): String = + joinToString(separator = "") \ No newline at end of file diff --git a/src/main/kotlin/blitz/str/MutMultiColoredMultiLineString.kt b/src/main/kotlin/blitz/str/MutMultiColoredMultiLineString.kt new file mode 100644 index 0000000..2786900 --- /dev/null +++ b/src/main/kotlin/blitz/str/MutMultiColoredMultiLineString.kt @@ -0,0 +1,109 @@ +package blitz.str + +import blitz.term.AnsiiMode + +class MutMultiColoredMultiLineString( + var fill: ColoredChar +) { + val lines = mutableListOf() + + // TODO: wrap at \n + + override fun equals(other: Any?): Boolean = + lines == other + + override fun hashCode(): Int = + lines.hashCode() + + /** if out of bounds, extends with @see fill */ + operator fun get(row: Int, col: Int): ColoredChar { + if (row >= lines.size) { + repeat(row - lines.size + 1) { + lines.add(MutMultiColoredString(fill = fill)) + } + } + return lines[row][col] + } + + /** if out of bounds, extends with @see fill */ + operator fun get(row: Int): MutMultiColoredString { + if (row >= lines.size) { + repeat(row - lines.size + 1) { + lines.add(MutMultiColoredString(fill = fill)) + } + } + return lines[row] + } + + /** if out of bounds, extends with @see fill */ + operator fun set(row: Int, col: Int, value: ColoredChar) { + if (row >= lines.size) { + repeat(row - lines.size + 1) { + lines.add(MutMultiColoredString(fill = fill)) + } + } else { + lines[row].fill = fill + } + lines[row][col] = value + } + + /** if out of bounds, extends with @see fill */ + operator fun set(row: Int, col: Int, value: Char) = + set(row, col, ColoredChar(value)) + + /** if out of bounds, extends with @see fill */ + operator fun set(row: Int, colStart: Int, value: CharSequence) { + if (row >= lines.size) { + repeat(row - lines.size + 1) { + lines.add(MutMultiColoredString(fill = fill)) + } + } else { + lines[row].fill = fill + } + lines[row][colStart] = value + } + + /** if out of bounds, extends with @see fill */ + operator fun set(row: Int, colStart: Int, value: MutMultiColoredString) { + if (row >= lines.size) { + repeat(row - lines.size + 1) { + lines.add(MutMultiColoredString(fill = fill)) + } + } else { + lines[row].fill = fill + } + lines[row][colStart] = value + } + + /** if out of bounds, extends with @see fill */ + fun set(row: Int, colStart: Int, va: Char, style: AnsiiMode) = + set(row, colStart, ColoredChar(va, style)) + + /** if out of bounds, extends with @see fill */ + fun set(row: Int, colStart: Int, va: String, style: AnsiiMode) = + set(row, colStart, MutMultiColoredString.from(va, style)) + + /** if out of bounds, extends with @see fill */ + operator fun set(rowStart: Int, colStart: Int, value: MutMultiLineString) { + value.lines.forEachIndexed { index, line -> + this[index + rowStart, colStart] = line + } + } + + /** if out of bounds, extends with @see fill */ + operator fun set(rowStart: Int, colStart: Int, value: MutMultiLineString, style: AnsiiMode) { + value.lines.forEachIndexed { index, line -> + this.set(index + rowStart, colStart, line.toString(), style) + } + } + + /** if out of bounds, extends with @see fill */ + operator fun set(rowStart: Int, colStart: Int, value: MutMultiColoredMultiLineString) { + value.lines.forEachIndexed { index, line -> + this[index + rowStart, colStart] = line + } + } + + override fun toString(): String = + lines.joinToString(separator = "\n") +} \ No newline at end of file diff --git a/src/main/kotlin/blitz/str/MutMultiColoredString.kt b/src/main/kotlin/blitz/str/MutMultiColoredString.kt new file mode 100644 index 0000000..916b263 --- /dev/null +++ b/src/main/kotlin/blitz/str/MutMultiColoredString.kt @@ -0,0 +1,92 @@ +package blitz.str + +import blitz.collections.mergeNeighbors +import blitz.term.AnsiiMode +import blitz.term.Terminal + +class MutMultiColoredString(var fill: ColoredChar) { + val chars = mutableListOf() + + val length + get() = chars.size + + override fun equals(other: Any?): Boolean = + chars == other + + override fun hashCode(): Int = + chars.hashCode() + + override fun toString(): String { + val byColors = chars.mergeNeighbors { it.style } + val res = MutString(fill = ' ') + byColors.forEach { + val style = it.first + val str = it.second.convToString() + res.append(Terminal.encodeString(str, style)) + } + return res.toString() + } + + fun add(str: String, style: AnsiiMode = AnsiiMode(mutableListOf())) { + str.mapTo(chars) { ColoredChar(it, style) } + } + + fun add(str: Iterable) { + chars.addAll(str) + } + + fun add(ch: ColoredChar) { + chars.add(ch) + } + + operator fun get(index: Int): ColoredChar { + if (index >= length) { + repeat(index - length + 1) { + chars.add(fill) + } + } + return chars[index] + } + + operator fun set(index: Int, value: ColoredChar) { + if (index >= length) { + repeat(index - length + 1) { + chars.add(fill) + } + } + chars[index] = value + } + + fun set(start: Int, str: CharSequence, style: AnsiiMode) { + if (start + str.length >= length) { + repeat(start + str.length - length + 1) { + chars.add(fill) + } + } + str.forEachIndexed { index, c -> + chars[start + index] = ColoredChar(c, style) + } + } + + operator fun set(start: Int, str: CharSequence) { + set(start, str, AnsiiMode(mutableListOf())) + } + + operator fun set(start: Int, str: MutMultiColoredString) { + if (start + str.length >= length) { + repeat(start + str.length - length + 1) { + chars.add(fill) + } + } + str.chars.forEachIndexed { index, c -> + chars[start + index] = c + } + } + + companion object { + fun from(str: String, style: AnsiiMode = AnsiiMode(mutableListOf())) = + MutMultiColoredString(fill = ColoredChar(' ')).also { + it.add(str, style) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/blitz/str/MutMultiLineString.kt b/src/main/kotlin/blitz/str/MutMultiLineString.kt index e4287ca..f2b5f59 100644 --- a/src/main/kotlin/blitz/str/MutMultiLineString.kt +++ b/src/main/kotlin/blitz/str/MutMultiLineString.kt @@ -7,6 +7,12 @@ class MutMultiLineString( // TODO: wrap at \n + override fun equals(other: Any?): Boolean = + lines == other + + override fun hashCode(): Int = + lines.hashCode() + /** if out of bounds, extends with @see fill */ operator fun get(row: Int, col: Int): Char { if (row >= lines.size) { diff --git a/src/main/kotlin/blitz/str/MutString.kt b/src/main/kotlin/blitz/str/MutString.kt index 5a0a61e..b5414a0 100644 --- a/src/main/kotlin/blitz/str/MutString.kt +++ b/src/main/kotlin/blitz/str/MutString.kt @@ -1,11 +1,19 @@ package blitz.str +import java.util.stream.IntStream + class MutString( init: String = "", var fill: Char ): CharSequence, Appendable { private val builder = StringBuilder(init) + override fun chars(): IntStream = + builder.chars() + + override fun codePoints(): IntStream = + builder.codePoints() + override val length: Int get() = builder.length @@ -30,12 +38,12 @@ class MutString( /** if out of bounds, extends with @see fill */ operator fun set(start: Int, str: CharSequence) { - if (start >= length) { - repeat(start - length + 1) { + if (start + str.length >= length) { + repeat(start + str.length - length + 1) { builder.append(fill) } } - builder.insert(start, str) + builder.replace(start, start + str.length, str.toString()) } override fun toString(): String = diff --git a/src/main/kotlin/blitz/term/AnsiiStr.kt b/src/main/kotlin/blitz/term/AnsiiStr.kt index 1475c07..05e9cb2 100644 --- a/src/main/kotlin/blitz/term/AnsiiStr.kt +++ b/src/main/kotlin/blitz/term/AnsiiStr.kt @@ -5,11 +5,17 @@ class AnsiiMode(internal val values: MutableList) { operator fun plus(other: AnsiiMode): AnsiiMode = AnsiiMode((values + other.values).toMutableList()) + + override fun equals(other: Any?): Boolean = + values == other + + override fun hashCode(): Int = + values.hashCode() } private val escape = (27).toChar() -fun ansiiStr(str: String, vararg modes: AnsiiMode) = +internal fun ansiiStr(str: String, vararg modes: AnsiiMode) = if (modes.isEmpty()) str else diff --git a/src/main/kotlin/blitz/term/Terminal.kt b/src/main/kotlin/blitz/term/Terminal.kt index d7801eb..c97dfc4 100644 --- a/src/main/kotlin/blitz/term/Terminal.kt +++ b/src/main/kotlin/blitz/term/Terminal.kt @@ -38,6 +38,23 @@ object Terminal { val darker by lazy { Color(AnsiiMode(darkerChannel(ch(fg))), AnsiiMode(darkerChannel(ch(bg)))) } } + /** Escape-sequence safe string length */ + fun len(str: String): Int { + var len = 0 + var ansii = false + str.forEach { + if (ansii) { + if (it == 'm') + ansii = false + } else if (it.code == 27) { + ansii = true + } else { + len ++ + } + } + return len + } + fun encodeString(str: String, vararg modes: AnsiiMode) = ansiiStr(str, *modes)