lib/text.ps1
|
# Text measuring and trimming, shared by more than one subsystem. # # Both functions here started life inside the one command that needed them -- # the sentence cutter in update.ps1, the width measurer in applystyle.ps1 -- # and a second caller is exactly the moment to move them. A copy in the second # place is the failure this repo keeps paying for: two implementations of one # rule drift, and the drift shows up as two different answers to the same # question in two parts of the same output. function Get-VisibleLength { # Printable width of a string that carries SGR escapes. The swatch and the # badges are mostly escape bytes, and measuring them with .Length overstates # their width by a factor of ten -- which would leave no room for anything # else and silently drop the description on every terminal. [CmdletBinding()] param([AllowNull()][string]$Text) if (-not $Text) { return 0 } ($Text -replace "$([char]27)\[[0-9;]*[A-Za-z]", '').Length } function Get-SentenceSummary { <# .SYNOPSIS The opening of a piece of prose, cut short enough to fit somewhere. .DESCRIPTION Two callers, one rule. Release notes in this project run to a thousand characters -- they are the PSGallery listing, written to be read on a web page -- and printing the whole thing after an update buries the one line that matters (that it worked). A style's description is two short sentences, and `tstyles list` has only what is left of the row after the swatch and the badge. Cut at a SENTENCE boundary rather than mid-word, and only if there is more than one sentence to cut. A summary that ends mid-clause reads like the output was truncated by accident. #> [CmdletBinding()] param([AllowNull()][string]$Notes, [int]$MaxLength = 220) $t = "$Notes".Trim() -replace '\s+', ' ' if (-not $t) { return '' } if ($t.Length -le $MaxLength) { return $t } # Find where sentences END and slice there, rather than gluing matches back # together. Two traps, both hit on the way here: # # * the terminator must be FOLLOWED by a space or end-of-string, or # "v0.8.32" is three sentences and the summary opens "v0. 8. 32:"; # * matches are not contiguous from the start, so concatenating their # values silently drops whatever the engine skipped -- that version # prefix came out as "32: two WezTerm compositions restored". # # Slicing the ORIGINAL string at an index cannot do either. $end = 0 foreach ($m in [regex]::Matches($t, '[.!?](?=\s|$)')) { $idx = $m.Index + 1 if ($idx -gt $MaxLength) { break } $end = $idx } if ($end -gt 0) { return $t.Substring(0, $end).Trim() } # One very long opening sentence: fall back to a hard cut, marked as one. return $t.Substring(0, [Math]::Max(1, $MaxLength - 1)).TrimEnd() + [char]0x2026 } |