# Changelog

All notable changes to this project will be documented in this file.

## v0.9.6 — 2026-08-02

### Fixed

- **Compiled-template cache key.** The on-disk and in-process caches now include
  the render context's helper names in the key. A template compiled without a
  context, or with a different set of helpers, is no longer reused for a render
  whose context resolves different bare identifiers.

### Changed

- **CI test command.** The workflow runs the staged runner (`./test.raku`) for
  each matrix stage instead of invoking the test paths directly.
- **Release process.** The install check in `RELEASE.md` uses
  `zef --force-install install .` so re-installing the same version from a
  working tree succeeds.

## v0.9.5 — 2026-06-27

### Added

- **Render context.** `HAML.render` accepts a `:context` named argument whose
  methods become reachable inside templates as bare identifiers in expression
  position and control-flow conditions. Resolution order is locals, then context
  method, then the normal Raku evaluator.
- **Helpers with arguments.** A context method is callable as a bare name with
  arguments (`!= link-to('Home', '/')`), dispatching to the context, in both the
  interpreted and direct-emit renderers.
- **User-provided helpers.** `Template::HAML::HelpersRole` and
  `Template::HAML::ViewContext` are the two composition points for adding
  template-facing helpers. Mix the role into your own class to keep the built-in
  helpers, or subclass `ViewContext`.
- **Overriding built-ins.** A context method takes precedence over the built-in
  helper of the same name, so replacing `surround`, `list-of`, `partial`, etc.
  for a render is just defining a method with that name.
- **Explicit helper declaration.** A `haml-helper-names` method lets a context
  declare helpers that `.^methods` introspection cannot see, such as those added
  at runtime or served through `FALLBACK`.

## v0.9.4 — 2026-05-31

### Changed

- **CI test runs.** The test suite is split across multiple parallel jobs so
  the pipeline finishes faster and failures are easier to localize.
- **CI benchmarks.** The benchmark run moved into its own dedicated job,
  separate from the test jobs.
- **Ecosystem metadata.** Packaging metadata tweaked so the distribution links
  correctly on raku.land.

## v0.9.3 — 2026-05-25

### Added

- **`:layout` for `render-file-cached`.** Mirrors `HAML.render(:file, :layout)`.
  Both the inner template and the layout are compiled, cached on disk, and
  memoized in-process independently; editing either file invalidates only that
  file's entry. The layout sees the inner render through `= yield` and
  `yield(:name<...>)` / `content-for`.

## v0.9.2 — 2026-05-25

### Changed

- **CI workflow.** Added a `name:` and `workflow_dispatch` trigger so the
  pipeline can be re-run manually from the GitHub Actions UI.
- **CI dependencies.** Dropped the explicit install step for `BDD::Behave`
  from git; it now resolves through normal `--test-depends` from the Raku
  ecosystem.

## v0.9.1 — 2026-05-23

### Added

- **Tag shorthand interpolation.** `#{...}` and `!{...}` inside class and id
  shorthand (`.#{cls}`, `##{id}`).
- **Object references.** `[obj]` / `[obj, :prefix]` after a tag emits stable
  `class` and `id` derived from the object's type and identity.
- **Attribute splats.** `*%h` and `*@a` inside `{...}` / `(...)` to merge hashes
  and pair-lists into the attribute set, with the same ordering and boolean
  rules as literal attributes.
- **Template codegen.** Compile a parsed template to a reusable Raku sub via
  `Template::HAML::Codegen`, cached on disk and keyed on source + config.
- **Direct-emit codegen.** `Template::HAML::DirectEmit` lowers templates to
  straight-line `print` / `write` calls for hot paths, bypassing the AST walker.
- **Streaming render.** `render-stream` writes incrementally to any `IO::Handle`
  or `Supplier`, with backpressure-aware chunking.
- **Cross-process precompilation cache.** Compiled templates persist under
  `.precomp/` keyed on path + mtime + config hash and are reused across
  processes.
- **Cache invalidation.** Mtime + config-hash checks invalidate stale entries;
  manual `Template::HAML::Cache.clear` for tests and dev loops.
- **Codegen error mapping.** Runtime exceptions thrown from generated code map
  back to original template `:file`, `:line`, `:column` via a side-table.
- **Render-time scope.** Locals, helpers, and `content-for` blocks share a
  single scope object so partials and layouts see consistent state.
- **Function memoization.** Pure helpers can opt in to per-render memoization
  with `is memoized`.
- **Plugin API.** `Template::HAML::Plugin` lets third-party packages register
  filters, helpers, doctypes, and tag transformers from a single entry point.
- **AST visitor hook.** `Template::HAML::Visitor` exposes pre- and post-order
  walks over the parsed tree for linters, formatters, and analyzers.
- **Tag transformer registry.** Register callbacks that rewrite tag nodes
  before codegen (e.g. auto-`rel="noopener"` on external links).
- **Pluggable markdown filter.** `:markdown` filter backed by any registered
  markdown implementation; ships with a default adapter.
- **`haml render`.** Render a template file or stdin to stdout / `-o`, with
  `--locals`, `--format`, `--escape-html` / `--no-escape-html`, `--ugly`.
- **`haml fmt`.** Canonicalize indentation, attribute order, and quote style;
  `--check` exits non-zero on diff for CI.
- **`haml lint`.** Static checks for duplicate ids, unused locals, illegal
  indentation, and unknown filters/doctypes.
- **`--watch` for CLI.** Re-renders on file change with debounce.
- **Trace for template debugging.** `TEMPLATE_HAML_TRACE=1` annotates output
  with source positions as HTML comments.
- **String encoding validation.** Non-UTF-8 input fails fast with a clear
  `X::HAML::ParseFail` instead of silently producing mojibake.
- **`page-class` helper.** Emits a stable `class` attribute for the document
  body based on controller/action or template path.
- **Bench harness.** `bench/` scripts for parse, codegen, and render timings
  against a fixed corpus.
- **Meta-provides testing.** `AUTHOR_TESTING=1` checks every module under
  `lib/` is listed in `META6.json` provides.

### Changed

- **Interior whitespace.** Runs of internal whitespace in plain text and
  attribute values are squeezed to a single space under `:ugly` output, matching
  Ruby HAML.

### Fixed

- **Grammar bugs.** Several edge cases around nested interpolation, attribute
  parsing, and indent re-entry after blank lines.
- **CI.** Test matrix and dependency installation on GitHub Actions.

## v0.9.0 — 2026-05-11

First release. Targets feature parity with Ruby HAML 5.x. Built on Raku
Grammars.

### Added

- **Grammar foundations.** Line-type dispatch (`tag`, `statement`, `comment`,
  `silent-comment`, `doctype`, `filter`, `plain`, `blank`), CRLF normalization,
  indent-unit auto-detection with strict mixed-tab/space and inconsistent-indent
  errors, and source positions (file/line/column) threaded through every
  `X::HAML::*` exception.
- **Tag syntax.** Implicit-div shorthand (`.foo`, `#bar`), combined explicit
  shorthand in any order (`%tag.a.b#main`), id concatenation with `_`,
  self-closing (explicit `/` and implicit for HTML void elements), inline-plus-
  block content, whitespace-removal modifiers (`>`, `<`, `<>`, `><`), XML
  namespaced names, and custom-element names containing `-`.
- **Attributes.** Hash form (`{...}`), HTML-style (`(...)`), old-style hash
  rocket (`{:foo => 'bar'}`), hyphenated string keys, `data:` / `aria:`
  shorthand with recursive expansion and optional camelCase → kebab-case,
  boolean and `Nil` value rules, HTML escaping by default, configurable quote
  style, and stable attribute ordering.
- **Embedded Raku code.** `=`, `-`, `!=`, `&=`, `==` output operators backed by
  a cached `EVAL` substrate; multiline continuation on trailing comma or
  unbalanced brackets; errors wrapped in `X::HAML::Eval` with source position.
- **Control flow.** `if` / `unless` / `elsif` / `else`, `for`, `while` (capped),
  `given` / `when` / `default`, and `repeat N`. Loop variables are scoped to
  the block.
- **Plain text and interpolation.** `#{...}` (escaped) and `!{...}` (raw) in
  tag content, attribute values, and plain text; backslash escape; no legacy
  `|` continuation.
- **Comments.** HTML comments (`/`), conditional comments (`/[if IE]`),
  revealed conditional, and silent comments (`-#`) whose children are opaque.
- **Doctypes.** `!!!` with `5`, `1.1`, `Strict`, `Frameset`, `Mobile`, `RDFa`,
  `Basic`, `XML`, and `XML <encoding>` variants; format-aware defaults.
- **Filters.** Pluggable registry (`Template::HAML.register-filter`) with
  built-ins: `:plain`, `:escaped`, `:javascript`, `:css`, `:cdata`,
  `:preserve`, `:raku`. Interpolation rules match Ruby HAML per filter.
- **Configuration.** `Template::HAML::Config` covering `format`
  (`:html5` / `:html4` / `:xhtml`), `escape_html`, `escape_attrs`,
  `output-style` (`:pretty` / `:ugly`), `autoclose`, `preserve`, `encoding`,
  `cdata`, `mime_type`, `suppress_eval`, and `attr_quote`.
- **Whitespace operators.** Outer (`>`), inner (`<`), combined, and `~ expr`
  to force preserve on a single tag; `find-and-preserve` helper.
- **Helpers.** `capture-haml`, `haml-concat`, `surround`, `precede`, `succeed`,
  `tab-up` / `tab-down`, `find-and-preserve`, `escape-once`, `html-safe`,
  `list-of`.
- **Layouts and partials.** `= yield` (named and unnamed), `content-for`,
  `= render :partial<name>` with `:locals`, `:collection` / `:as`, search-path
  resolution, and a per-process compile cache keyed on path + mtime.
- **Errors.** `X::HAML::ParseFail`, `IndentMixed`, `IndentInconsistent`,
  `UnknownFilter`, `UnknownDoctype`, `Eval`, `TemplateNotFound`, `DuplicateId`,
  `IllegalIndent`, each carrying `:file`, `:line`, `:column`, `:snippet`.
  Parse failures render a one-line caret-pointer snippet.
- **CLI.** `haml render` (with `-o`, `--locals`, `--format`,
  `--escape-html` / `--no-escape-html`, `--ugly`), `haml check`, `haml --help`,
  and per-subcommand help.
