fix(view): underline and open a badge the row cut - #74
Merged
Conversation
A badge whose config names a link is drawn underlined and opens when clicked. Where the row ran out of width and cut that badge, it was drawn without the underline and the terminal was never told about the link, so the reader lost the destination along with the columns. The cut takes columns off what a link says and nothing off where it goes, and the whole escape sequence lives in the cell the link starts on, so the columns the cut took were never columns the sequence needed. A cut badge now keeps its underline and opens the same destination a fitted one opens, round the head the row kept. Where the cut leaves the badge no columns at all there is nothing to be told round, and no link is opened. `bead_line` pushed a link for every badge whose config named one while `badge_style` underlined only the ones the emitter would write. That mismatch was harmless while a cut badge lost both, because the emitter was asked again at the last moment and refused. It is not harmless now: a badge whose whole text the emitter refuses can have a head it would accept, which would open unmarked. Both sites ask `opens_at` of the badge's own text, so a badge is a link or it is not, at every width.
Hand-weakening the change found two holes the mutation run could not. The run offered `bead_line` one mutant, which did not compile, and `surviving` only whole-body replacements alongside its operators, so neither function's internals were scored. A badge whose whole text the emitter refuses can have a head it would accept, and drawing that head as an unmarked live link is the inverse of the lie the cut used to tell. Swept over every width, because which width cuts back to a clean head falls out of a control character's own zero columns. A cut link's cell reports the head's width rather than the badge's, and the diff skips exactly that many columns behind it. Hardcoding the whole badge's width left the suite green, because the width was pinned only where nothing was cut. One weakening survives on purpose, for whoever runs a gate over this code next. Deleting `cut_to`'s `!head.is_empty()` guard changes nothing a test can see: an empty span draws nothing, and `Kept::told_to` refuses a zero width through `NonZeroU16`. The guard keeps `cut_to`'s count honest at the layer that knows what it cut, and the `NonZeroU16` guards the write in a function that does not know. Two guards each honest about their own layer beat one that has to know about both, so neither is weakened to make a tally read clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A badge whose config names a link draws underlined and opens when clicked. Where the row ran out of width and cut that badge,
bditook the underline off and told the terminal nothing, so the reader lost the destination along with the columns. That is the wrong trade. A cut is not the case5d276f0was reasoning about: the destination is still known and the terminal can still open it, so a clipped label on a live link beats refusing both. A badge the emitter genuinely refuses still draws plain and dead, because there the old reasoning holds.fitandcut_toreported how many spans the block kept whole, and that count is what dropped a cut link; they now report how many reached the row. None of the emitting machinery needed changing, because the whole escape sequence already lived in the cell the link starts on — so the columns the cut took were never columns the sequence needed.The part worth a reviewer's attention is
opens_at, which the diff makes look like tidying.bead_linepushed a link for every badge whose config named one, whilebadge_styleunderlined only those the emitter would write. That mismatch was harmless while a cut badge lost both, because the emitter is asked again at the last moment and refuses. It stops being harmless here: a badge whose whole text the emitter refuses can have a head it would accept, and that head would open with no underline on it, which is the inverse of the lie this change is fixing. Both sites now ask one function of the badge's own text.Of the tests the old behaviour rested on, two invert and one is left exactly as it was, because what it pins — that the cut reaches a badge's underline and nothing else — is still true.
The mutation gate over the first commit reported 44 mutants, 40 caught, 4 unviable, none missed. Read that figure knowing
bead_linewas scored by nothing at all, its only mutant beingDefault::default()on a return type that has none. Hand-weakening found two real holes, both closed in the second commit:opens_atwas untested outright, and a cut link's forced width was pinned only where nothing was cut, so hardcoding the whole badge's five columns stayed green. That width is what tells the diff how many columns to skip behind the link's cell. The second commit is tests only, so the figure still stands for this tree.One weakening survives and is left alone on purpose. The reasoning is in the second commit's message, where a future gate-runner will find it.
0.7.0 gains a release-notes line for this, written by the seat that owns that file. Nothing under
RELEASE-NOTES/is touched here.