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

kyo.Ansi.scala Maven / Gradle / Ivy

There is a newer version: 0.12.2
Show newest version
package kyo

import scala.util.control.NonFatal

/** Provides ANSI color and formatting utilities for strings.
  */
object Ansi:

    extension (str: String)
        /** Applies black color to the string. */
        def black: String = s"\u001b[30m$str\u001b[0m"

        /** Applies red color to the string. */
        def red: String = s"\u001b[31m$str\u001b[0m"

        /** Applies green color to the string. */
        def green: String = s"\u001b[32m$str\u001b[0m"

        /** Applies yellow color to the string. */
        def yellow: String = s"\u001b[33m$str\u001b[0m"

        /** Applies blue color to the string. */
        def blue: String = s"\u001b[34m$str\u001b[0m"

        /** Applies magenta color to the string. */
        def magenta: String = s"\u001b[35m$str\u001b[0m"

        /** Applies cyan color to the string. */
        def cyan: String = s"\u001b[36m$str\u001b[0m"

        /** Applies white color to the string. */
        def white: String = s"\u001b[37m$str\u001b[0m"

        /** Applies grey color to the string. */
        def grey: String = s"\u001b[90m$str\u001b[0m"

        /** Applies bold formatting to the string. */
        def bold: String = s"\u001b[1m$str\u001b[0m"

        /** Applies dim formatting to the string. */
        def dim: String = s"\u001b[2m$str\u001b[0m"

        /** Applies italic formatting to the string. */
        def italic: String = s"\u001b[3m$str\u001b[0m"

        /** Applies underline formatting to the string. */
        def underline: String = s"\u001b[4m$str\u001b[0m"

        /** Removes all ANSI escape sequences from the string. */
        def stripAnsi: String = str.replaceAll("\u001b\\[[0-9;]*[a-zA-Z]", "")
    end extension

    object highlight:

        /** Applies syntax highlighting to a code snippet with optional header and trailer.
          *
          * @param header
          *   The header text to be displayed before the code (optional).
          * @param code
          *   The main code snippet to be highlighted.
          * @param trailer
          *   The trailer text to be displayed after the code (optional).
          * @param startLine
          *   The starting line number for the code snippet (default is 1).
          * @return
          *   A string with the highlighted code, including line numbers and formatting.
          */
        def apply(header: String, code: String, trailer: String, startLine: Int = 1): String =
            try
                val headerLines  = if header.nonEmpty then header.split("\n") else Array.empty[String]
                val codeLines    = code.split("\n").dropWhile(_.trim.isEmpty).reverse.dropWhile(_.trim.isEmpty).reverse
                val trailerLines = if trailer.nonEmpty then trailer.split("\n") else Array.empty[String]

                val allLines        = headerLines ++ codeLines ++ trailerLines
                val toDrop          = codeLines.filter(_.trim.nonEmpty).map(_.takeWhile(_ == ' ').length).minOption.getOrElse(0)
                val lineNumberWidth = (startLine + codeLines.length - 1).toString.length
                val separator       = "│".dim

                val processedLines = allLines.zipWithIndex.map { case (line, index) =>
                    val isHeader  = index < headerLines.length
                    val isTrailer = index >= (headerLines.length + codeLines.length)
                    val lineNumber =
                        if isHeader || isTrailer then
                            " ".repeat(lineNumberWidth)
                        else
                            (startLine + index - headerLines.length).toString.padTo(lineNumberWidth, ' ')

                    val highlightedLine =
                        if isHeader || isTrailer then
                            line.green
                        else
                            highlightLine(line.drop(toDrop))

                    s"${lineNumber.dim} $separator $highlightedLine"
                }

                processedLines.mkString("\n")
            catch
                case ex if NonFatal(ex) =>
                    (header :: code :: trailer :: Nil).mkString("\n")
        end apply

        /** Applies syntax highlighting to a code snippet without header or trailer.
          *
          * @param code
          *   The code snippet to be highlighted.
          * @return
          *   A string with the highlighted code, including line numbers and formatting.
          */
        def apply(code: String): String = apply("", code, "", 1)

        private def highlightLine(line: String): String =
            if line.trim.startsWith("//") then
                line.green // Make entire line green for single-line comments
            else
                line.split(" ").map { token =>
                    if keywords.contains(token) then token.yellow
                    else if token.matches("\".*\"") then token.green
                    else if token.matches("/\\*.*\\*/") then token.green // Inline multi-line comments
                    else if token.matches("[0-9]+") then token.cyan
                    else token
                }.mkString(" ")
        end highlightLine

        private val keywords = Set(
            "abstract",
            "case",
            "catch",
            "class",
            "def",
            "do",
            "else",
            "enum",
            "export",
            "extends",
            "final",
            "finally",
            "for",
            "given",
            "if",
            "implicit",
            "import",
            "lazy",
            "match",
            "new",
            "object",
            "override",
            "package",
            "private",
            "protected",
            "return",
            "sealed",
            "super",
            "throw",
            "trait",
            "try",
            "type",
            "val",
            "var",
            "while",
            "with",
            "yield"
        )
    end highlight
end Ansi




© 2015 - 2024 Weber Informatics LLC | Privacy Policy