From 355289716b7b9c746f71a2d2d3abe4e237baf2d6 Mon Sep 17 00:00:00 2001 From: alex-s168 <63254202+alex-s168@users.noreply.github.com> Date: Fri, 29 Mar 2024 09:54:28 +0100 Subject: [PATCH] code errors - init --- src/main/kotlin/blitz/codeerrors/Errors.kt | 122 ++++++++++++++++++ .../kotlin/blitz/str/MutMultiLineString.kt | 20 +++ 2 files changed, 142 insertions(+) create mode 100644 src/main/kotlin/blitz/codeerrors/Errors.kt diff --git a/src/main/kotlin/blitz/codeerrors/Errors.kt b/src/main/kotlin/blitz/codeerrors/Errors.kt new file mode 100644 index 0000000..083e520 --- /dev/null +++ b/src/main/kotlin/blitz/codeerrors/Errors.kt @@ -0,0 +1,122 @@ +package blitz.codeerrors + +import blitz.str.MutMultiLineString +import blitz.str.MutString +import blitz.term.AnsiiMode +import blitz.term.Terminal + +object Errors { + data class Source( + val file: String, + val content: MutMultiLineString + ) + + data class Location( + val source: Source, + val line: Int, + val col: Int, + val size: Int, + ) + + data class Error( + val message: String, + val level: Level, + val loc: Location, + val isHint: Boolean = false, + ) { + enum class Level { + INFO, + WARN, + ERROR, + ; + + operator fun plus(other: Level): Level = + when (this) { + INFO -> other + WARN -> if (other == ERROR) other else this + ERROR -> this + } + } + } + + data class PrintConfig( + val styles: Map = mapOf( + Error.Level.INFO to Terminal.COLORS.WHITE.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" + } + }, + val underlineString: (len: Int) -> String = { + if (it == 0) "" else "^" + "~".repeat(it - 1) + }, + ) + + fun print(config: PrintConfig, errors: Iterable) { + val bySources = errors.groupBy { it.loc.source } + bySources.forEach { (source, 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]!!) + + val perLines = errors + .groupBy { it.loc.line } + .entries + .sortedBy { it.key } + + perLines.forEach { (line, errors) -> + errors.asSequence().filterNot { it.isHint }.forEach { err -> + Terminal.err(config.levelStr(err.level), config.styles[err.level]!!, Terminal.STYLES.BOLD) + Terminal.errln(": ${err.message}", 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] = '|' + nextCol += 2 + if (line > 0) + msg[0, nextCol] = source.content[line - 1] + msg[1, nextCol] = source.content[line] + + // TODO: underline + + // TODO: hints + + Terminal.errln(msg.toString()) + } + + Terminal.errln("================================================================================", config.styles[worst]!!) + } + } +} + +fun main() { + val source = Errors.Source("main.kt", MutMultiLineString.from(""" + fn main() { + return 1 + } + """.trimIndent(), ' ')) + + val errors = listOf( + Errors.Error( + "Cannot return integer from function with return type void", + Errors.Error.Level.ERROR, + Errors.Location(source, 1, 11, 1) + ) + ) + + val config = Errors.PrintConfig() + + Errors.print(config, errors) +} \ 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 f68c196..e4287ca 100644 --- a/src/main/kotlin/blitz/str/MutMultiLineString.kt +++ b/src/main/kotlin/blitz/str/MutMultiLineString.kt @@ -17,6 +17,16 @@ class MutMultiLineString( return lines[row][col] } + /** if out of bounds, extends with @see fill */ + operator fun get(row: Int): MutString { + if (row >= lines.size) { + repeat(row - lines.size + 1) { + lines.add(MutString(fill = fill)) + } + } + return lines[row] + } + /** if out of bounds, extends with @see fill */ operator fun set(row: Int, col: Int, value: Char) { if (row >= lines.size) { @@ -50,4 +60,14 @@ class MutMultiLineString( override fun toString(): String = lines.joinToString(separator = "\n") + + companion object { + fun from(str: String, fill: Char): MutMultiLineString { + val res = MutMultiLineString(fill) + str.lines().forEach { + res.lines += MutString(it, fill) + } + return res + } + } } \ No newline at end of file