max / quasi
- Co-Authored-By
- Claude Opus 5 (1M context) <noreply@anthropic.com>
- Claude-Session
- https://claude.ai/code/session_01P8ostB2UmZJGj5WjSHRSot
3 files changed,
+76 insertions,
-5 deletions
| @@ -6314,6 +6314,10 @@ | |||
| 6314 | 6314 | "winnow 1.0.4", | |
| 6315 | 6315 | ] | |
| 6316 | 6316 | ||
| 6317 | + | [[patch.unused]] | |
| 6318 | + | name = "synckit-client" | |
| 6319 | + | version = "0.10.0" | |
| 6320 | + | ||
| 6317 | 6321 | [[patch.unused]] | |
| 6318 | 6322 | name = "kberg" | |
| 6319 | 6323 | version = "0.1.0" | |
| @@ -6330,10 +6334,6 @@ | |||
| 6330 | 6334 | name = "tagtree" | |
| 6331 | 6335 | version = "0.4.1" | |
| 6332 | 6336 | ||
| 6333 | - | [[patch.unused]] | |
| 6334 | - | name = "synckit-client" | |
| 6335 | - | version = "0.10.0" | |
| 6336 | - | ||
| 6337 | 6337 | [[patch.unused]] | |
| 6338 | 6338 | name = "quasi-type" | |
| 6339 | 6339 | version = "0.1.3" |
| @@ -68,6 +68,47 @@ | |||
| 68 | 68 | } | |
| 69 | 69 | } | |
| 70 | 70 | ||
| 71 | + | declare! { | |
| 72 | + | /// The same listing with the dispatch written inline, and a cell after it. | |
| 73 | + | /// | |
| 74 | + | /// MNW's feed is this shape rather than the one below: the dispatch sits in | |
| 75 | + | /// the loop body of the table's own shape, with cells either side of it and | |
| 76 | + | /// an `activate` on the row. The version below puts the row in a shape of | |
| 77 | + | /// its own, which gives the dispatch a scope to itself and a position at | |
| 78 | + | /// the end -- so it never exercised what follows an arm. | |
| 79 | + | #[staged] | |
| 80 | + | pub(crate) shape inline_listing(items: &[Item]) -> Node; | |
| 81 | + | ||
| 82 | + | table { | |
| 83 | + | column "Name" { | |
| 84 | + | width Fill; | |
| 85 | + | } | |
| 86 | + | column "Price" { | |
| 87 | + | width Content; | |
| 88 | + | } | |
| 89 | + | column "Date" { | |
| 90 | + | width Content; | |
| 91 | + | } | |
| 92 | + | ||
| 93 | + | for item in items.iter() { | |
| 94 | + | cells { | |
| 95 | + | cell at "Name" item.name.clone(); | |
| 96 | + | ||
| 97 | + | given item.free { | |
| 98 | + | true -> cell at "Price" "" { | |
| 99 | + | token Tag::badge("Free").tone(layout::Tone::Success); | |
| 100 | + | } | |
| 101 | + | otherwise -> cell at "Price" price(item); | |
| 102 | + | } | |
| 103 | + | ||
| 104 | + | cell at "Date" item.name.clone(); | |
| 105 | + | ||
| 106 | + | activate to get "/i/{item.name}" navigating; | |
| 107 | + | } | |
| 108 | + | } | |
| 109 | + | } | |
| 110 | + | } | |
| 111 | + | ||
| 71 | 112 | declare! { | |
| 72 | 113 | /// One row. The price cell is a dispatch, not a pair of guards. | |
| 73 | 114 | /// | |
| @@ -106,6 +147,18 @@ | |||
| 106 | 147 | /// | |
| 107 | 148 | /// The old failure stated as a test: exactly one case reached the residual | |
| 108 | 149 | /// and the rest were gone, so a residual holding only one is the bug. | |
| 150 | + | /// A dispatch with a cell after it, which is the feed's shape. | |
| 151 | + | #[test] | |
| 152 | + | fn an_arm_is_followed_by_the_cells_written_after_it() { | |
| 153 | + | let webview = Webview::new(); | |
| 154 | + | let residual = quasi_webview::stage::derive(&Webview::new(), inline_listing_staged); | |
| 155 | + | let rows = items(&[true, false, true]); | |
| 156 | + | assert_eq!( | |
| 157 | + | webview.fragment(&inline_listing(&rows)), | |
| 158 | + | inline_listing_serve(&residual, &rows), | |
| 159 | + | ); | |
| 160 | + | } | |
| 161 | + | ||
| 109 | 162 | #[test] | |
| 110 | 163 | fn both_cases_reach_the_residual() { | |
| 111 | 164 | let residual = residual(); |
| @@ -684,6 +684,17 @@ | |||
| 684 | 684 | Arms(Cow<'static, [Cow<'static, [Op]>]>), | |
| 685 | 685 | } | |
| 686 | 686 | ||
| 687 | + | /// What one op is, for a message a reader can act on. | |
| 688 | + | fn describe(op: &Op) -> &'static str { | |
| 689 | + | match op { | |
| 690 | + | Op::Lit(_) => "a literal", | |
| 691 | + | Op::Hole { .. } => "a hole", | |
| 692 | + | Op::Branch(_) => "a branch, which a guard makes", | |
| 693 | + | Op::Loop(_) => "a loop, which a `for` makes", | |
| 694 | + | Op::Arms(_) => "a dispatch, which `given` or a settling setting makes", | |
| 695 | + | } | |
| 696 | + | } | |
| 697 | + | ||
| 687 | 698 | /// A screen's markup with the request taken out of it. | |
| 688 | 699 | /// | |
| 689 | 700 | /// # Why the instructions are borrowed or owned | |
| @@ -1173,7 +1184,14 @@ | |||
| 1173 | 1184 | self.literals(out); | |
| 1174 | 1185 | assert!( | |
| 1175 | 1186 | self.at == self.ops.len(), | |
| 1176 | - | "the filler stopped before the residual did" | |
| 1187 | + | "the filler stopped before the residual did: {} of {} ops walked, and \ | |
| 1188 | + | the first one left is {}. A structural op left over means the residual \ | |
| 1189 | + | holds a branch, a loop or a dispatch that the fill program has no \ | |
| 1190 | + | instruction for, which is the two halves of one shape disagreeing \ | |
| 1191 | + | about its structure rather than anything a request did.", | |
| 1192 | + | self.at, | |
| 1193 | + | self.ops.len(), | |
| 1194 | + | self.ops.get(self.at).map_or("nothing", describe), | |
| 1177 | 1195 | ); | |
| 1178 | 1196 | } | |
| 1179 | 1197 | } |