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

replpp.shaded.pprint.PPrinter.scala Maven / Gradle / Ivy

There is a newer version: 0.1.98
Show newest version
package replpp.shaded.pprint
import replpp.shaded.fansi
import replpp.shaded.sourcecode

import java.io.PrintStream

/**
  *
  * @param defaultWidth How wide to allow a pretty-printed value to become
  *                     before wrapping
  * @param defaultHeight How tall to allow the pretty-printed output to become
  *                      before truncated it with a `...`
  * @param defaultIndent How many spaces to indent each nested [[Tree.Apply]] by
  * @param colorLiteral What color to assign to literals like `"lol"` or 31337
  * @param colorApplyPrefix What color to assign to `Foo` in `Foo(bar, baz)`
  * @param additionalHandlers Provide this to override how certain types are
  *                           pretty-printed at runtime
  */
case class PPrinter(defaultWidth: Int = 100,
                    defaultHeight: Int = 500,
                    defaultIndent: Int = 2,
                    defaultEscapeUnicode: Boolean = false,
                    defaultShowFieldNames: Boolean = true,
                    colorLiteral: replpp.shaded.fansi.Attrs = fansi.Color.Green,
                    colorApplyPrefix: replpp.shaded.fansi.Attrs = fansi.Color.Yellow,
                    additionalHandlers: PartialFunction[Any, Tree] = PartialFunction.empty)
  extends Walker{ outer =>

  /**
    * Logs a given value to stdout with some metadata to identify where the log
    * message came from. Hard-coded and not very flexible, but you can easily
    * implement your own log method if you want to customize it further.
    */
  def log[T](x: sourcecode.Text[T],
             tag: String = "",
             width: Int = defaultWidth,
             height: Int = defaultHeight,
             indent: Int = defaultIndent,
             escapeUnicode: Boolean = defaultEscapeUnicode,
             showFieldNames: Boolean = defaultShowFieldNames)
            (implicit line: sourcecode.Line,
             fileName: sourcecode.FileName): T = {
    doLog(x, tag, width, height, indent, escapeUnicode, showFieldNames, Console.out)(line, fileName)
  }

  object err {
    /**
      * Logs a given value to stderr with some metadata to identify where the log
      * message came from. Hard-coded and not very flexible, but you can easily
      * implement your own log method if you want to customize it further.
      */
    def log[T](x: sourcecode.Text[T],
               tag: String = "",
               width: Int = defaultWidth,
               height: Int = defaultHeight,
               indent: Int = defaultIndent,
               escapeUnicode: Boolean = defaultEscapeUnicode,
               showFieldNames: Boolean = defaultShowFieldNames)
              (implicit line: sourcecode.Line,
               fileName: sourcecode.FileName): T = {
      doLog(x, tag, width, height, indent, escapeUnicode, showFieldNames, Console.err)(line, fileName)
    }
  }

  private def doLog[T](x: sourcecode.Text[T],
                       tag: String,
                       width: Int,
                       height: Int,
                       indent: Int,
                       escapeUnicode: Boolean,
                       showFieldNames: Boolean,
                       out: PrintStream)
                      (implicit line: sourcecode.Line,
                       fileName: sourcecode.FileName): T = {
    val tagStrs =
      if (tag.isEmpty) Seq()
      else Seq(fansi.Color.Cyan(tag), fansi.Str(" "))

    val prefix = Seq(
      fansi.Color.Magenta(fileName.value),
      fansi.Str(":"),
      fansi.Color.Green(line.value.toString),
      fansi.Str(" "),
      fansi.Color.Cyan(x.source),
      fansi.Str(": ")
    ) ++ tagStrs
    val str = fansi.Str.join(
      prefix ++
      tokenize(
        x.value,
        width,
        height,
        indent,
        escapeUnicode = escapeUnicode,
        showFieldNames = showFieldNames
      ).toSeq
    )

    out.println(str)
    x.value
  }

  /**
    * Converts an [[Any]] into a large colored `fansi.Str`
    */
  def apply(x: Any,
            width: Int = defaultWidth,
            height: Int = defaultHeight,
            indent: Int = defaultIndent,
            initialOffset: Int = 0,
            escapeUnicode: Boolean = defaultEscapeUnicode,
            showFieldNames: Boolean = defaultShowFieldNames): fansi.Str = {
    fansi.Str.join(
      this.tokenize(
        x,
        width,
        height,
        indent,
        initialOffset,
        escapeUnicode = escapeUnicode,
        showFieldNames = showFieldNames
      ).toSeq
    )
  }

  /**
    * Converts an [[Any]] into a large colored `fansi.Str`
    */
  def pprintln[T](x: T,
                  width: Int = defaultWidth,
                  height: Int = defaultHeight,
                  indent: Int = defaultIndent,
                  initialOffset: Int = 0,
                  escapeUnicode: Boolean = defaultEscapeUnicode,
                  showFieldNames: Boolean = defaultShowFieldNames): Unit = {
    tokenize(
      x,
      width,
      height,
      indent,
      initialOffset,
      escapeUnicode = escapeUnicode,
      showFieldNames = showFieldNames
    ).foreach(print)
    println()
  }

  /**
    * Converts an [[Any]] into an iterator of colored chunks, wrapped at a
    * certain width and truncated at a certain height
    */
  def tokenize(x: Any,
               width: Int = defaultWidth,
               height: Int = defaultHeight,
               indent: Int = defaultIndent,
               initialOffset: Int = 0,
               escapeUnicode: Boolean = defaultEscapeUnicode,
               showFieldNames: Boolean = defaultShowFieldNames): Iterator[fansi.Str] = {

    // The three stages within the pretty-printing process:

    // Convert the Any into a lazy Tree of `Apply`, `Infix` and `Lazy`/`Strict` literals
    val tree = this.treeify(x, escapeUnicode, showFieldNames)
    // Render the `Any` into a stream of tokens, properly indented and wrapped
    // at the given width
    val renderer = new Renderer(width, colorApplyPrefix, colorLiteral, indent)
    val rendered = renderer.rec(tree, initialOffset, 0).iter
    // Truncate the output stream once it's wrapped-at-width height goes
    // beyond the desired height
    val truncated = new Truncated(rendered, width, height)
    truncated
  }
}

object PPrinter {
  object Color extends PPrinter
  object BlackWhite extends PPrinter(
    colorLiteral = fansi.Attrs.Empty,
    colorApplyPrefix = fansi.Attrs.Empty
  )
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy