1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
//! Text analysis helpers for Svelte template whitespace.
//!
//! Newline/blank-line predicates over `str`, used by the printer's blank-line decisions.
//! (Whitespace *collapsing* itself lives in the doc-based content path —
//! `build_text_fill_doc_trimmed` and friends.)
//!
//! ⚠️ **Three "is there a blank line" predicates exist, and they are NOT interchangeable.**
//! Each answers a different question; picking the wrong one is how a formatter fabricates
//! whitespace the author never wrote. Name the question at the call site:
//!
//! | predicate | question | shape |
//! | --- | --- | --- |
//! | [`has_authored_blank_line`] | did the author write a blank line *somewhere* in this text? | run, anywhere |
//! | [`has_leading_blank_line`] | did the author write a blank line *before this text's first content*? | run, leading only |
//! | [`has_trailing_blank_line`] | did the author write a blank line *after this text's last content*? | run, trailing only |
//!
//! The last two are the two halves of one question — *is there a blank line at this SEAM* — and
//! a caller that has a side in hand (a boundary's `leading` flag) picks between them rather than
//! reaching for the `anywhere` scan: a blank *inside* the text belongs to the text, and relaying
//! it to the seam relocates the author's blank rather than preserving it.
//!
//! All three scan for a **run** — two newlines separated by horizontal whitespace only. A newline
//! *total* (`count >= 2`) is not a fourth option: two SEPARATE single breaks (`\ntext1\ntext2`)
//! reach it with no blank line present, so a total coincides with the authoring signal only on a
//! string that is nothing but whitespace, and every call site that would reach such a
//! predicate is a string with content in it — trailing text after a `format-ignore`
//! range end, a range marker between a section comment and its section. Don't introduce it: a
//! caller that "knows" its string is pure whitespace is one refactor away from being wrong. The
//! near miss to watch for is a caller that has already sliced an edge run
//! ([`crate::ast::text_edge_ws`]) and counts newlines in *that* — correct only because
//! the slice is pure whitespace by construction, which is the same one refactor away. Ask the
//! seam predicate instead; it takes the whole `raw` and does its own scoping.
//!
//! A module doc rather than a plain comment on purpose: the table's intra-doc links are then
//! checked by `docs:audit`, so a rename can't rot it silently.
/// Whether `raw` holds an authored blank line **anywhere** — two newlines separated by
/// horizontal whitespace only.
///
/// The Tier-2 authoring signal for "did the author leave a blank line inside this text".
/// Contrast the two seam predicates, [`has_leading_blank_line`] and
/// [`has_trailing_blank_line`], which ask the same of one edge whitespace run alone.
pub
/// Whether `raw` **opens** with an authored blank line — two newlines, separated by horizontal
/// whitespace only, before any content byte.
///
/// The question a *seam* asks: "did the author leave a blank line immediately after the thing
/// that precedes this text?" Scoping to the leading run is what separates that from
/// [`has_authored_blank_line`] — a blank *inside* the text (`\ntext1\n\ntext2`) belongs to the
/// text, not to the seam, so relaying it to the seam would relocate the author's blank rather
/// than preserve it.
///
/// Identical to [`has_authored_blank_line`] except that a content byte ENDS the answer instead
/// of resetting the run. On a whitespace-only string the two therefore agree — the leading run
/// is the whole string.
///
/// Kept as its own scan rather than folded into its siblings. Both folds are correct and both
/// were measured to GROW `.text` (`objcopy -O binary --only-section=.text target/corpus/tsv`,
/// against this pair): delegating to
/// `has_authored_blank_line(ast::text_edge_ws(raw, true))` costs +128 bytes, and one
/// `const LEADING_ONLY: bool` scan behind two wrappers costs +112 — `#[inline]` recovers
/// neither. The same behaviour-neutral-but-not-code-neutral trade the `is_horizontal_ws` note
/// below records, and declined for the same reason, [`has_trailing_blank_line`] included. What
/// the duplication actually buys back is small: the bodies differ in exactly one `match` arm
/// (and, for the trailing one, the scan direction), each doc comment names the others, and
/// `leading_blank_line_stops_at_the_first_content_byte` /
/// `trailing_blank_line_stops_at_the_last_content_byte` pin both the case where they differ and
/// the whitespace-only case where they must agree.
pub
/// Whether `raw` **ends** with an authored blank line — two newlines, separated by horizontal
/// whitespace only, after the last content byte.
///
/// The mirror of [`has_leading_blank_line`], asked at the seam that FOLLOWS a text rather than
/// the one that precedes it: "did the author leave a blank line immediately before the thing
/// that follows this text?" Scoped to the trailing run for the same reason — a blank *inside*
/// the text (`text1\n\ntext2\n`) belongs to the text, not to the seam.
///
/// Identical to [`has_leading_blank_line`] with the scan reversed; see that one for why the
/// three bodies are hand-rolled rather than folded. On a whitespace-only string all three agree.
pub
/// Whether `b` is **horizontal** collapsible whitespace — whitespace that does not end a line.
///
/// [`crate::ast::is_collapsible_ws`] minus the line feed (`[ \t\r]`). The callers are
/// the three run scans above: does this byte let the newline run continue rather than end it. A
/// form feed is content, so it breaks the run — an FF between two newlines is not a blank line,
/// matching the compiler's own class (`regex_not_whitespace` = `/[^ \t\r\n]/`).
///
/// Spelled as the explicit set rather than `ast::is_collapsible_ws(b) && b != b'\n'` — the same
/// set, in the form the scans' `match` arms want.
///
/// A second spelling of this set lives on `text_starts_with_linebreak` in `fragment_doc.rs`. It is
/// deliberately left alone: it feeds a `str` pattern, and swapping the `[char; 3]` array for a
/// predicate fn changes the `Pattern` monomorphization (a measured `.text` growth), so folding it
/// in is a behaviour-neutral-but-not-code-neutral change rather than part of this cleanup.