Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/scripts/extract-changelog.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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++
Expand Down
35 changes: 35 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
34 changes: 34 additions & 0 deletions docs/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 12 additions & 1 deletion docs/RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down