Files
thor/WARNING_COUNTER.md

2.9 KiB
Raw Permalink Blame History

Warning Counter Plan

Goal

When the same warning fires multiple times across a single page render, show it once with a [×N] count prefix instead of N identical diagnostic blocks.

[×5] first -3 is equivalent to last 3 — consider using the clearer form
 --> layouts/home.html:12:1
  |
12 | <ul>{{#items | first -3}}<li>{{.}}</li>{{/items}}</ul>
   | ^^^^^^^^^^^^^^^^^^^^^ consider using the clearer form
   |

Approach

Module-level buffer in pipes.odin. Warnings are collected during render, deduplicated + counted + logged after the page completes. No parameter threading through render_nodes.

Changes

mustache/pipes.odin

New module-level state:

_pending_warnings: [dynamic]Error

New procs:

reset_warnings :: proc() {
    clear(&_pending_warnings)
}

flush_warnings :: proc(colorize: bool = false) {
    if len(_pending_warnings) == 0 do return

    counts := make(map[string]int, context.temp_allocator)
    first: map[string]Error = make(map[string]Error, context.temp_allocator)
    defer delete(counts)
    defer delete(first)

    for w in _pending_warnings {
        b := body(w)
        if _, seen := first[b.msg]; !seen {
            first[b.msg] = w
        }
        counts[b.msg] += 1
    }

    for msg, w in first {
        c := counts[msg]
        b := body(w)
        path := b.path != "" ? b.path : "<input>"
        prefix := c > 1 ? fmt.tprintf("[×%d] ", c) : ""
        diag := diags.format_error(
            path, b.source, b.pos, b.msg,
            hint = b.hint,
            colorize = colorize,
        )
        log.warnf("%s%s", prefix, diag)
    }

    clear(&_pending_warnings)
}

apply_pipeline change:

Replace the current warning handling (which logs immediately):

if warnings != nil {
    append(warnings, ferr)
} else {
    log.warnf("%s", format_render_error(ferr, tmpl))
}

With:

append(&_pending_warnings, ferr)

Drop the warnings: ^[dynamic]Error parameter — no longer needed.

render.odin

In render_template, wrap the render call:

mustache.reset_warnings()
result, err := mustache.render(content_tpl, []any{ctx.site, ctx.page, ctx}, partials)
mustache.flush_warnings(colorize = diags.should_colorize())

mustache/pipes.odin cleanup

  • Remove warnings parameter from apply_pipeline signature
  • Remove format_render_error import/call from apply_pipeline (formatting moves to flush_warnings)
  • Remove the log import if no longer used directly in apply_pipeline

Scope

~25 lines new code (reset_warnings + flush_warnings), ~3 lines changed in apply_pipeline, ~2 lines added in render.odin. Total ~30 lines.

Limitations

  • Module-level state means warnings don't survive across page renders (by design — reset_warnings is called per page)
  • Not thread-safe (fine for sequential rendering)
  • Counting is by exact msg string — warnings with different messages but same root cause are counted separately