All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlinx.jupyter.exceptions.ReplCompilerException.kt Maven / Gradle / Ivy

Go to download

Implementation of REPL compiler and preprocessor for Jupyter dialect of Kotlin (IDE-compatible)

The newest version!
package org.jetbrains.kotlinx.jupyter.exceptions

import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import org.jetbrains.kotlinx.jupyter.repl.CellErrorMetaData
import kotlin.script.experimental.api.ResultWithDiagnostics
import kotlin.script.experimental.api.ScriptDiagnostic

private fun enhanceReplCompilerError(
    metadata: CellErrorMetaData?,
    message: String,
): String {
    if (metadata == null || message.isEmpty()) return message
    // Possible patterns we need to look out for:
    // - Line_0.jupyter.kts (1:4 - 4) Some message
    val pattern = "Line_\\d+\\.jupyter\\.kts \\((?\\d+):(?\\d+) - \\d+\\) (?.*)".toRegex()
    return message.lines().joinToString("\n") { line ->
        pattern.find(line)?.let { match ->
            val lineNumber: Int = match.groups["line"]!!.value.toInt()
            val columnNumber: Int = match.groups["column"]!!.value.toInt()
            val msg = match.groups["message"]!!.value
            // In this case, we also show the line number even if it is outside the visible
            // range, since hiding it would make debugging harder in case of bugs in compiler
            // plugins that modify the code.
            "at Cell In[${metadata.executionCount}], line $lineNumber, column $columnNumber: $msg"
        } ?: line
    }
}

/**
 * Exception type for compile time errors happening in the user's code.
 */
class ReplCompilerException(
    val failedCode: String,
    val errorResult: ResultWithDiagnostics.Failure? = null,
    message: String? = null,
    metadata: CellErrorMetaData? = null,
) : ReplException(
        enhanceReplCompilerError(metadata, message ?: errorResult?.getErrors() ?: ""),
        errorResult?.reports?.map { it.exception }?.firstOrNull(),
    ) {
    val firstError: ScriptDiagnostic? =
        errorResult?.reports?.firstOrNull {
            it.severity == ScriptDiagnostic.Severity.ERROR || it.severity == ScriptDiagnostic.Severity.FATAL
        }

    override fun getAdditionalInfoJson(): JsonObject? {
        return firstError?.location?.let {
            val errorMessage = firstError.message
            JsonObject(
                mapOf(
                    "lineStart" to JsonPrimitive(it.start.line),
                    "colStart" to JsonPrimitive(it.start.col),
                    "lineEnd" to JsonPrimitive(it.end?.line ?: -1),
                    "colEnd" to JsonPrimitive(it.end?.col ?: -1),
                    "message" to JsonPrimitive(errorMessage),
                    "path" to JsonPrimitive(firstError.sourcePath.orEmpty()),
                ),
            )
        }
    }

    override fun render() = message
}

fun  ResultWithDiagnostics.getErrors(): String {
    val filteredReports =
        reports.filter {
            it.code != ScriptDiagnostic.incompleteCode
        }

    return filteredReports.joinToString("\n") { report ->
        report.location?.let { loc ->
            report.sourcePath?.let { sourcePath ->
                compilerDiagnosticToString(
                    sourcePath,
                    loc.start.line,
                    loc.start.col,
                    loc.end?.line ?: -1,
                    loc.end?.col ?: -1,
                )
            }?.let {
                "$it "
            }
        }.orEmpty() + report.message
    }
}

fun compilerDiagnosticToString(
    path: String,
    line: Int,
    column: Int,
    lineEnd: Int,
    columnEnd: Int,
): String {
    val start =
        if (line == -1 && column == -1) {
            ""
        } else {
            "$line:$column"
        }
    val end =
        if (lineEnd == -1 && columnEnd == -1) {
            ""
        } else if (lineEnd == line) {
            " - $columnEnd"
        } else {
            " - $lineEnd:$columnEnd"
        }
    val loc = if (start.isEmpty() && end.isEmpty()) "" else " ($start$end)"
    return path + loc
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy