mirror of
https://github.com/sbrow/thor.git
synced 2026-10-25 10:11:55 -04:00
test: Converted DIAGNOSTIC_TODOS.yaml into a test suite.
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
# 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:**
|
||||
```odin
|
||||
_pending_warnings: [dynamic]Error
|
||||
```
|
||||
|
||||
**New procs:**
|
||||
```odin
|
||||
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):
|
||||
```odin
|
||||
if warnings != nil {
|
||||
append(warnings, ferr)
|
||||
} else {
|
||||
log.warnf("%s", format_render_error(ferr, tmpl))
|
||||
}
|
||||
```
|
||||
|
||||
With:
|
||||
```odin
|
||||
append(&_pending_warnings, ferr)
|
||||
```
|
||||
|
||||
Drop the `warnings: ^[dynamic]Error` parameter — no longer needed.
|
||||
|
||||
### `render.odin`
|
||||
|
||||
In `render_template`, wrap the render call:
|
||||
```odin
|
||||
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
|
||||
Reference in New Issue
Block a user