diff --git a/cli/src/main/kotlin/tools/samt/cli/DiagnosticFormatter.kt b/cli/src/main/kotlin/tools/samt/cli/DiagnosticFormatter.kt index e40b2781..bb91c5f4 100644 --- a/cli/src/main/kotlin/tools/samt/cli/DiagnosticFormatter.kt +++ b/cli/src/main/kotlin/tools/samt/cli/DiagnosticFormatter.kt @@ -115,20 +115,40 @@ internal class DiagnosticFormatter( DiagnosticSeverity.Info -> formatTextForSeverity("INFO:", severity, withBold = true) } - private fun formatGlobalMessage(message: DiagnosticGlobalMessage): String = buildString { - append(formatSeverityIndicator(message.severity)) + private fun formatSeverityAndMessage(severity: DiagnosticSeverity, message: String): String = buildString { + // : + append(formatSeverityIndicator(severity)) append(" ") - append(message.message) - appendLine() + + // these magic numbers relate to the amount of space each severity indicator takes up when formatted + // e.g. "ERROR: ".length == 7 + // "WARNING: ".length == 9 + // "INFO: ".length == 6 + val indentLength = when (severity) { + DiagnosticSeverity.Error -> 7 + DiagnosticSeverity.Warning -> 9 + DiagnosticSeverity.Info -> 6 + } + + val lines = message.lines() + if (lines.size > 1) { + lines.forEachIndexed { index, it -> + if (index > 0) { + append(" ".repeat(indentLength)) + } + appendLine(it) + } + } else { + appendLine(message) + } } - private fun formatMessage(message: DiagnosticMessage, context: DiagnosticContext): String = buildString { + private fun formatGlobalMessage(message: DiagnosticGlobalMessage): String = buildString { + append(formatSeverityAndMessage(message.severity, message.message)) + } - // : - append(formatSeverityIndicator(message.severity)) - append(" ") - append(message.message) - appendLine() + private fun formatMessage(message: DiagnosticMessage, context: DiagnosticContext): String = buildString { + append(formatSeverityAndMessage(message.severity, message.message)) val errorSourceFilePath = if (message.highlights.isNotEmpty()) { message.highlights.first().location.source.path diff --git a/cli/src/test/kotlin/tools/samt/cli/DiagnosticFormatterTest.kt b/cli/src/test/kotlin/tools/samt/cli/DiagnosticFormatterTest.kt index 052054b4..8b85d52a 100644 --- a/cli/src/test/kotlin/tools/samt/cli/DiagnosticFormatterTest.kt +++ b/cli/src/test/kotlin/tools/samt/cli/DiagnosticFormatterTest.kt @@ -478,6 +478,48 @@ class DiagnosticFormatterTest { """.trimIndent().trim(), outputWithoutColors.trimIndent().trim()) } + @Test + fun `multiline error messages`() { + val source = """ + package debug + enum Test { + Foo + } + """.trimIndent() + + val (fileNode, context, controller) = parse(source) + assert(fileNode.statements.single() is EnumDeclarationNode) + val enumNode = fileNode.statements.single() as EnumDeclarationNode + + context.error { + message("some error\nsome error\nsome error") + highlight("some highlight", enumNode.location, highlightBeginningOnly = true) + } + + val output = DiagnosticFormatter.format(controller, 0, 0, terminalWidth = 40) + val outputWithoutColors = output.replace(Regex("\u001B\\[[;\\d]*m"), "") + + assertEquals(""" + ──────────────────────────────────────── + ERROR: some error + some error + some error + ---> file:///tmp/DiagnosticFormatterTest.samt:2:1 + + 1 │ package debug + 2 │ enum Test { + │ ^ + │ | + │ some highlight + │ + 3 │ Foo + 4 │ } + + ──────────────────────────────────────── + FAILED in 0ms (1 error(s), 0 warning(s)) + """.trimIndent().trim(), outputWithoutColors.trimIndent().trim()) + } + private fun parse(source: String): Triple { val baseDirectory = URI("file:///tmp") val filePath = URI("file:///tmp/DiagnosticFormatterTest.samt")