code errors - init
This commit is contained in:
122
src/main/kotlin/blitz/codeerrors/Errors.kt
Normal file
122
src/main/kotlin/blitz/codeerrors/Errors.kt
Normal file
@@ -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<Error.Level, AnsiiMode> = 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<Error>) {
|
||||
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)
|
||||
}
|
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user