| 146 |
146 |
|
let mut spans = Vec::new();
|
| 147 |
147 |
|
for &(scope, id) in &sites.loops {
|
| 148 |
148 |
|
let wider = render(&base_plan.clone().with_rows_at(scope, id, 2));
|
| 149 |
|
- |
if let Some(span) = grown(&base, &wider) {
|
| 150 |
|
- |
spans.push(Span {
|
| 151 |
|
- |
repeats: true,
|
| 152 |
|
- |
..span
|
| 153 |
|
- |
});
|
| 154 |
|
- |
}
|
|
149 |
+ |
let Some(span) = grown(&base, &wider) else {
|
|
150 |
+ |
unheld("loop", scope, id, &base, &wider);
|
|
151 |
+ |
};
|
|
152 |
+ |
spans.push(Span {
|
|
153 |
+ |
repeats: true,
|
|
154 |
+ |
..span
|
|
155 |
+ |
});
|
| 155 |
156 |
|
}
|
| 156 |
157 |
|
for &(scope, id) in &sites.guards {
|
| 157 |
158 |
|
let without = render(&base_plan.clone().with_guard_at(scope, id, false));
|
| 158 |
|
- |
if let Some(span) = shrunk(&base, &without) {
|
| 159 |
|
- |
spans.push(Span {
|
| 160 |
|
- |
repeats: false,
|
| 161 |
|
- |
..span
|
| 162 |
|
- |
});
|
| 163 |
|
- |
}
|
|
159 |
+ |
let Some(span) = shrunk(&base, &without) else {
|
|
160 |
+ |
unheld("guard", scope, id, &base, &without);
|
|
161 |
+ |
};
|
|
162 |
+ |
spans.push(Span {
|
|
163 |
+ |
repeats: false,
|
|
164 |
+ |
..span
|
|
165 |
+ |
});
|
| 164 |
166 |
|
}
|
| 165 |
167 |
|
|
| 166 |
168 |
|
Residual::new(tree(&base, &mut spans))
|
| 167 |
169 |
|
}
|
| 168 |
170 |
|
|
|
171 |
+ |
/// Refuse a site the residual has no shape for, and say which kind it is.
|
|
172 |
+ |
///
|
|
173 |
+ |
/// Reached when varying one guard or one loop does not change the render the
|
|
174 |
+ |
/// way a gap changes it. Three things can be behind that, and the message
|
|
175 |
+ |
/// separates them because the fix is different for each.
|
|
176 |
+ |
///
|
|
177 |
+ |
/// # Why this is loud here rather than left to the filler
|
|
178 |
+ |
///
|
|
179 |
+ |
/// The filler already refuses the result: a residual with no branch where the
|
|
180 |
+ |
/// fill program has a guard panics in [`Cursor::branch`]. But it refuses it on a
|
|
181 |
+ |
/// REQUEST, so a screen without a filling test ships and takes its first reader
|
|
182 |
+ |
/// down, and it can be quiet instead -- two sites that both fail this way and
|
|
183 |
+ |
/// whose bodies happen to have the same shape line up, and the filler answers
|
|
184 |
+ |
/// one site's branch with the other's arm. Refusing here moves both cases to the
|
|
185 |
+ |
/// build machine, where the residual is generated.
|
|
186 |
+ |
///
|
|
187 |
+ |
/// The commonest cause by a distance is a **substitution**: markup that replaces
|
|
188 |
+ |
/// other markup rather than being absent. A guard derives as a branch only when
|
|
189 |
+ |
/// turning it off deletes bytes, so a renderer that draws one thing when a
|
|
190 |
+ |
/// value is present and a different thing when it is not gives the derivation
|
|
191 |
+ |
/// nothing to measure. `rest_html` drew a disabled control for a pager direction
|
|
192 |
+ |
/// with no address until 2026-09-08, and `rest_strip_html` still draws the page
|
|
193 |
+ |
/// a reader is on as a readout rather than a control. Both are two markups at
|
|
194 |
+ |
/// one position, which is what `Op::Arms` is for.
|
|
195 |
+ |
#[cold]
|
|
196 |
+ |
fn unheld(kind: &str, scope: u32, id: u16, base: &str, varied: &str) -> ! {
|
|
197 |
+ |
let what = if base == varied {
|
|
198 |
+ |
"it renders exactly the same either way, so it places nothing and the \
|
|
199 |
+ |
residual has no gap to hold. A member that is sometimes nothing is a \
|
|
200 |
+ |
member whose value is sometimes empty, and empty is a different shape \
|
|
201 |
+ |
rather than a shorter one"
|
|
202 |
+ |
} else if base.len() == varied.len() {
|
|
203 |
+ |
"the two renders are the same length and differ in their bytes, which \
|
|
204 |
+ |
is a substitution rather than a gap: the screen is saying two markups \
|
|
205 |
+ |
at one position"
|
|
206 |
+ |
} else {
|
|
207 |
+ |
"what changes is not a clean insertion or deletion, so it is a \
|
|
208 |
+ |
substitution rather than a gap: the screen is saying two markups at \
|
|
209 |
+ |
one position, and a residual holds one"
|
|
210 |
+ |
};
|
|
211 |
+ |
panic!(
|
|
212 |
+ |
"this screen cannot be staged: {kind} {id} in scope {scope} varies the \
|
|
213 |
+ |
render in a way a residual cannot hold, because {what}.\n\nWith the \
|
|
214 |
+ |
{kind} on, the render is {} bytes; with it off, {}.",
|
|
215 |
+ |
base.len(),
|
|
216 |
+ |
varied.len(),
|
|
217 |
+ |
);
|
|
218 |
+ |
}
|
|
219 |
+ |
|
| 169 |
220 |
|
/// The span `wide` holds one more time than `narrow` does, in `narrow`.
|
| 170 |
221 |
|
///
|
| 171 |
222 |
|
/// `narrow` is `P R Q` and `wide` is `P R R Q`, so the extra copy is the length
|