---
name: pretty
description: Use when writing or changing a Pretty typeclass instance (paiges Doc DSL) in scalus-core, for example in scalus/utils/Pretty.scala or the cardano/ledger types.
---

# Pretty Printer Writing Guide

This skill provides knowledge for implementing `Pretty[A]` typeclass instances in the Scalus
codebase.

## Key Files

- `scalus-core/shared/src/main/scala/scalus/utils/Pretty.scala` - typeclass definition and helpers
- `scalus-core/shared/src/main/scala/scalus/cardano/ledger/*.scala` - example instances

## Pretty Typeclass Overview

The `Pretty[A]` typeclass converts values to `org.typelevel.paiges.Doc` for flexible pretty
printing.

### Key Methods

- `pretty(a: A, style: Style): Doc` - concise format
- `prettyDetailed(a: A, style: Style): Doc` - detailed format (optional override)

### Creating Instances

```scala
// Simple instance
given Pretty[MyType] with
    def pretty(a: MyType, style: Style): Doc = ...

// Instance with detailed view
given Pretty[MyType] = Pretty.instanceWithDetailed(
  concise = (a, style) => ...,
  detailed = (a, style) => ...
)
```

## Doc DSL Quick Reference

### Basic Combinators

- `text("str")` - literal text
- `str(value)` - convert to string then Doc
- `char('x')` - single character
- `empty` - empty Doc
- `space` - single space
- `comma` - comma character
- `line` - line break (or space when flattened)

### Composition Operators

- `a + b` - concatenate Docs directly (no space)
- `a & b` - concatenate Docs with space
- `a / b` - concatenate Docs with line break

### Layout Control

- `.nested(n)` - indent by n after line breaks
- `.indent(n)` - indent the whole thing
- `.hang(n)` - hanging indent (first line not indented)
- `.grouped` - try to fit on one line, break if needed
- `.flatten` - force single line

### Lists and Brackets

- `fill(sep, docs)` - fill paragraphs with flexible breaks
- `stack(docs)` - vertical stack (join with line breaks)
- `intercalate(sep, docs)` - join with separator
- `.tightBracketBy(open, close)` - wrap with brackets
- `.bracketBy(open, close)` - wrap with brackets and indent

## Pretty Helpers (from `scalus.utils.Pretty`)

### Bracket Helpers

- `Pretty.inParens(d)` - wrap in `(d)`
- `Pretty.inBraces(d)` - wrap in `{d}`
- `Pretty.inBrackets(d)` - wrap in `[d]`
- `Pretty.braceList(entries)` - `{ a, b, c }` format

### Field Helpers

- `Pretty.field(label, value, style)` - `label: value` with styled label
- `Pretty.optField(label, opt, style)` - optional field
- `Pretty.bulletList(label, items)` - bulleted list

### Styling (XTerm colors)

- `Pretty.kw(s, style)` - keyword (orange) - use for labels
- `Pretty.ctr(s, style)` - constructor (blue) - use for type names
- `Pretty.lit(d, style)` - literal (green) - use for values
- `Pretty.typ(d, style)` - type (purple)
- `Pretty.err(d, style)` - error (red)

### Rainbow Brackets (16 colors)

For nested structures, use rainbow brackets to color brackets based on nesting depth:

- `Pretty.rainbowChar(c, depth, style)` - single bracket char with rainbow color
- `Pretty.rainbowBracket(d, open, close, depth, style)` - wrap content in rainbow brackets

Colors cycle through 16 distinct XTerm colors: red → orange → yellow → lime → green → teal → cyan →
blue → indigo → purple → magenta → pink. Colors are chosen to avoid collision with `ctr`, `lit`, and
`kw` styling.

## Implementation Guidelines

When writing a `Pretty` instance:

1. **Read the type definition** to understand its structure

2. **Choose the right pattern:**
    - Enums/sealed traits: pattern match and show variant-specific content
    - Collections: use `fill` or `stack` for items
    - Wrappers: delegate to inner type's Pretty instance

3. **Apply styling consistently:**
    - `ctr` for constructor/type names
    - `lit` for literal values (numbers, booleans)
    - `kw` for field labels

4. Prefer using Composition Operators: &, /, + for clarity

5. **Use layout control:**
    - `.grouped` for content that should try to fit on one line
    - `fill(comma + space, items)` for lists that can wrap
    - `.hang(2)` or `.indent(2)` for nested content

6. **Add to companion object:**
   ```scala
   object MyType:
       import Doc.*
       import Pretty.{ctr, inParens, lit}

       given Pretty[MyType] with
           def pretty(a: MyType, style: Style): Doc = ...
   ```

7. **Delegate to other instances:**
   ```scala
   Pretty[OtherType].pretty(value, style)
   ```

8. **Test output:**
    - `.show` - render at 80 columns
    - `.showDetailed` - render detailed at 80 columns
    - `.showHighlighted` - render with XTerm colors
    - `.showDetailedHighlighted` - render detailed with XTerm colors

## Common Patterns

### Enum/Sealed Trait

```scala
given Pretty[MyEnum] with
    def pretty(a: MyEnum, style: Style): Doc = a match
        case MyEnum.VariantA(x) => ctr("VariantA", style) + inParens(lit(str(x), style))
        case MyEnum.VariantB(items) =>
            val inner = fill(comma + space, items.map(Pretty[Item].pretty(_, style)))
            (ctr("VariantB", style) + inParens(inner)).grouped
```

### Case Class with Fields

```scala
given Pretty[MyCaseClass] with
    def pretty(a: MyCaseClass, style: Style): Doc =
        val fields = List(
            field("name", text(a.name), style),
            field("value", lit(str(a.value), style), style)
        )
        (ctr("MyCaseClass", style) / stack(fields).indent(2)).grouped
```

### Concise + Detailed Views

```scala
given Pretty[MyType] = Pretty.instanceWithDetailed(
  concise = (a, style) => text(a.hash.toHex),
  detailed = (a, style) => detailedInfo
)
```

### Recursive Types with Rainbow Brackets

For nested/recursive structures, track depth and use rainbow brackets:

```scala
given Pretty[MyTree] with
    def pretty(a: MyTree, style: Style): Doc = prettyWithDepth(a, style, 0)

    private def prettyWithDepth(a: MyTree, style: Style, depth: Int): Doc = a match
        case Leaf(value) => lit(str(value), style)
        case Branch(left, right) =>
            val inner = prettyWithDepth(left, style, depth + 1) + comma + space +
                        prettyWithDepth(right, style, depth + 1)
            rainbowBracket(inner, '(', ')', depth, style)
```

### Guidelines

- Never shorten hashes, always print full hash. No `hash.take(16) + "..."`.