# `MingaEditor.Window.RenderCache`
[🔗](https://github.com/jsmestad/minga/blob/main/lib/minga_editor/window/render_cache.ex#L1)

Per-window render state for incremental rendering.

Tracks which buffer lines need re-rendering, caches draw commands from
previous frames, and stores last-frame comparison values so the render
pipeline can detect when a full redraw is needed.

## Dirty-line tracking

The dirty set uses two representations:

- `:all` means every line needs re-rendering (scroll, resize, theme
  change, highlight update, fold toggle, or any other wholesale
  invalidation)
- A map of specific buffer line numbers (`%{line => true}`) for targeted
  invalidation (edits that touch a few lines)

Gutter and content caches are separate because cursor movement with
relative line numbering dirties every gutter entry without changing
content. This avoids re-rendering line text when only line numbers change.

## Tracking fields

`last_viewport_top`, `last_gutter_w`, `last_line_count`, `last_cursor_line`,
and `last_buf_version` store values from the previous frame. The Scroll
stage compares current values against these to detect full-invalidation
triggers. `last_context_fingerprint` captures all per-frame render context
inputs (visual selection, search matches, syntax highlights, signs, etc.)
so context changes trigger full redraws.

# `context_fingerprint`

```elixir
@type context_fingerprint() :: term()
```

Context fingerprint: a term derived from the render context that
captures all per-frame inputs affecting every visible line. When
the fingerprint changes between frames, all lines are re-rendered.

Built from: visual selection, search matches, highlight version,
diagnostic signs, git signs, viewport left scroll, active status,
and theme color structs.

# `t`

```elixir
@type t() :: %MingaEditor.Window.RenderCache{
  cached_content: %{
    optional(non_neg_integer()) =&gt; [MingaEditor.DisplayList.draw()]
  },
  cached_gutter: %{
    optional(non_neg_integer()) =&gt; [MingaEditor.DisplayList.draw()]
  },
  dirty_lines: :all | %{optional(non_neg_integer()) =&gt; true},
  last_buf_version: integer(),
  last_context_fingerprint: context_fingerprint(),
  last_cursor_line: integer(),
  last_gutter_w: integer(),
  last_line_count: integer(),
  last_viewport_top: integer()
}
```

# `cache_line`

```elixir
@spec cache_line(t(), non_neg_integer(), [MingaEditor.DisplayList.draw()], [
  MingaEditor.DisplayList.draw()
]) :: t()
```

Stores rendered gutter and content draws for a buffer line.

Does NOT remove the line from the dirty set; that happens in
`snapshot/7` when the full frame is complete.

# `detect_context_change`

```elixir
@spec detect_context_change(t(), context_fingerprint()) :: t()
```

Compares the current render context fingerprint against the last frame's.

If the fingerprint changed, marks all lines dirty. Catches changes to
visual selection, search matches, syntax highlights, diagnostic signs,
git signs, horizontal scroll, active/inactive status, and theme colors.

# `detect_invalidation`

```elixir
@spec detect_invalidation(
  t(),
  non_neg_integer(),
  non_neg_integer(),
  non_neg_integer(),
  non_neg_integer()
) :: t()
```

Checks current frame parameters against last-frame tracking fields
and marks all lines dirty if anything requiring a full redraw has changed.

Structural triggers: viewport scroll, gutter width, line count, buffer
version, first frame (sentinel values).

# `dirty?`

```elixir
@spec dirty?(t(), non_neg_integer()) :: boolean()
```

Returns true if the given buffer line needs re-rendering.

# `mark_dirty`

```elixir
@spec mark_dirty(t(), [non_neg_integer()] | :all) :: t()
```

Marks specific buffer lines as needing re-render.

Pass `:all` to force a complete redraw. Pass a list of buffer line
numbers for targeted invalidation. If already fully dirty, adding
specific lines is a no-op.

# `prune`

```elixir
@spec prune(t(), non_neg_integer(), non_neg_integer()) :: t()
```

Prunes cache entries for buffer lines no longer in the visible range.

Keeps the cache bounded to avoid memory growth as the user scrolls
through a large file.

# `reset`

```elixir
@spec reset() :: t()
```

Returns a fresh cache with all lines dirty and no cached draws.

Use after any event that invalidates all cached draws: buffer switch,
resize, theme change, etc.

# `snapshot`

```elixir
@spec snapshot(
  t(),
  non_neg_integer(),
  non_neg_integer(),
  non_neg_integer(),
  non_neg_integer(),
  non_neg_integer(),
  context_fingerprint()
) :: t()
```

Snapshots tracking fields after a successful render pass.

Clears the dirty set and records the current frame's parameters so the
next frame can detect what changed.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
