diff --git a/.github/scripts/extract-changelog.sh b/.github/scripts/extract-changelog.sh index a0caf3b..e73a34b 100755 --- a/.github/scripts/extract-changelog.sh +++ b/.github/scripts/extract-changelog.sh @@ -23,6 +23,11 @@ result="$(awk -v ver="$version" ' if (found) exit if (index($0, "[" ver "]") > 0) { found = 1; next } } + # Keep a Changelog ends with link reference definitions, which sit below + # the oldest version heading and therefore inside its section as far as + # the slice above is concerned. They belong to the file, not to any one + # release, so collecting stops at the first of them (#319). + found && /^\[[^]]+\]:[[:space:]]+http/ { exit } found { lines[++n] = $0 } END { start = 1; while (start <= n && lines[start] ~ /^[[:space:]]*$/) start++ diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a44e60..d59556e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,18 @@ reads that marker. could not (#312). A path that is not an existing directory is refused by name, in one line, with exit 2. +### Changed + +- Every version heading in this file now links to its compare view, as + [Keep a Changelog] specifies and this file's own header claimed (#319). + `[Unreleased]` compares the newest tag to `HEAD`. Release notes are + unaffected: `extract-changelog.sh` stops collecting at the definitions, + so the notes for every published version are byte-identical to before. + +- `docs/DESIGN.md` §3 now names `Screen::parse` and says what a round trip + through the text format preserves and what it does not (#305). The spec + had never mentioned its own reader. + ## [0.11.0] - 2026-09-11 ### Changed @@ -1801,3 +1813,26 @@ in the worst of them — hung itself. instant-exit caveat in `docs/DESIGN.md`) an output-loss race in the OS pty teardown, found by the stress workflow at roughly 1 in 80 instant-exit spawns on macOS. + +[Keep a Changelog]: https://keepachangelog.com/en/1.1.0/ +[`Unsupported`]: https://docs.rs/termlens/0.11.0/termlens/struct.Unsupported.html + +[Unreleased]: https://github.com/vyncint/termlens/compare/v0.11.0...HEAD +[0.11.0]: https://github.com/vyncint/termlens/compare/v0.10.3...v0.11.0 +[0.10.3]: https://github.com/vyncint/termlens/compare/v0.10.2...v0.10.3 +[0.10.2]: https://github.com/vyncint/termlens/compare/v0.10.1...v0.10.2 +[0.10.1]: https://github.com/vyncint/termlens/compare/v0.10.0...v0.10.1 +[0.10.0]: https://github.com/vyncint/termlens/compare/v0.9.0...v0.10.0 +[0.9.0]: https://github.com/vyncint/termlens/compare/v0.8.0...v0.9.0 +[0.8.0]: https://github.com/vyncint/termlens/compare/v0.7.0...v0.8.0 +[0.7.0]: https://github.com/vyncint/termlens/compare/v0.6.1...v0.7.0 +[0.6.1]: https://github.com/vyncint/termlens/compare/v0.6.0...v0.6.1 +[0.6.0]: https://github.com/vyncint/termlens/compare/v0.5.0...v0.6.0 +[0.5.0]: https://github.com/vyncint/termlens/compare/v0.4.2...v0.5.0 +[0.4.2]: https://github.com/vyncint/termlens/compare/v0.4.0...v0.4.2 +[0.4.0]: https://github.com/vyncint/termlens/compare/v0.3.0...v0.4.0 +[0.3.0]: https://github.com/vyncint/termlens/compare/v0.2.1...v0.3.0 +[0.2.1]: https://github.com/vyncint/termlens/compare/v0.2.0...v0.2.1 +[0.2.0]: https://github.com/vyncint/termlens/compare/v0.1.1...v0.2.0 +[0.1.1]: https://github.com/vyncint/termlens/compare/v0.1.0...v0.1.1 +[0.1.0]: https://github.com/vyncint/termlens/releases/tag/v0.1.0 diff --git a/docs/DESIGN.md b/docs/DESIGN.md index eb7f39f..3ca3d9a 100644 --- a/docs/DESIGN.md +++ b/docs/DESIGN.md @@ -682,6 +682,40 @@ The `insta` feature (default) re-exports `insta` and ships `assert_screen_snapshot!` so the snapshotting insta version can't drift from the one the macro targets. +### Reading it back + +`Screen::parse` (0.10.0) is the reader for everything above: it accepts +exactly what `Display` and `with_styles` write — the header, the grid, and +optionally the blank line, `styles:` and its spans — and every other line is +an `Error::Parse` naming it. The `termlens` CLI is built on it: `diff` and +`render` take saved screens, so a snapshot outlives the test run that +produced it. + +The round trip is exact **for what the format carries**. A parsed screen +renders to the same text, with the same `styles:` block, and `diff`s empty +against the original. It is not `==` to the original, and rule 5's last +paragraph is why: equality is "the same observation" and the text format is +deliberately blind to most of it. What does not survive is exactly what the +rules above never wrote down — an erased cell and a written blank are one +character either way; the out-of-band state, the title, the mode flags, the +counters and the history, comes back default; and a hidden cursor's position +is not recorded, so `cursor: hidden` parses back to a hidden cursor at +`0,0`. `diff` reports none of those as a difference, which is the same claim +from the other side: the text format is the *picture*, and two screens that +render alike are the same picture. + +The header's row count is what decides where the grid ends — not a search +for the `styles:` marker — so a grid may hold the words `styles:` or +`(none)` as ordinary content. The one ambiguous input is a snapshot whose +trailing blank rows were trimmed by hand *and* which carries a styles block: +its block falls inside the declared row count and is read as content. +Content wins on purpose, because a wrong row of text shows up in a diff and +a silently dropped one does not. + +The JSON shape below is the format for the other job — carrying a `Screen` +whole, out-of-band state included, so that reading it back gives one that +compares `==`. + ### What a reader skips, and what it does not The format proper begins at the `size:` header and ends with the grid or diff --git a/docs/RELEASING.md b/docs/RELEASING.md index 4c10ec2..35766e7 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -76,9 +76,20 @@ $EDITOR Cargo.toml crates/termlens-cli/Cargo.toml cargo check --workspace # refreshes Cargo.lock # 2. Move the CHANGELOG section. A `- **Breaking:**` bullet moves with it, -# which is what lets the tag's semver run pass (see above). +# which is what lets the tag's semver run pass (see above). Then add the +# new version's link definition at the foot of the file and repoint +# [Unreleased] at the tag about to be cut — a Keep a Changelog convention +# that is not in this checklist decays on its first release (#319): +# +# [Unreleased]: …/compare/vX.Y.Z...HEAD <- was vPREV...HEAD +# [X.Y.Z]: …/compare/vPREV...vX.Y.Z <- new line, under it +# $EDITOR CHANGELOG.md # [Unreleased] -> [X.Y.Z] - YYYY-MM-DD # add a fresh empty [Unreleased] above + # and two lines at the foot +# Release notes stop above the link definitions, so they are unchanged by +# this; `extract-changelog.sh X.Y.Z` prints exactly the section. +.github/scripts/extract-changelog.sh X.Y.Z # what the GitHub Release will say # 2b. Freeze this release's saved-screen shapes into the compatibility # corpus (crates/termlens/tests/compat/README.md), from this tree: