@@ -967,6 +967,71 @@ const MEASURE_OVERSCAN_PX: f32 = 800.0;
967967/// layout even when the cursor briefly outruns the scroll position).
968968const MEASURE_OVERSCAN_LINES : usize = 30 ;
969969
970+ /// The 11 parallel per-line output vectors accumulated by `DocLayout::build`, kept in
971+ /// one struct so a placeholder row (hidden line or image-block anchor) is a single
972+ /// `push_placeholder` call instead of 11 hand-aligned pushes — the "add a field, forget
973+ /// a vector" hazard. Destructured into `DocLayout`'s fields at construction.
974+ struct Bands {
975+ heights : Vec < f32 > ,
976+ layouts : Vec < Rc < parley:: Layout < Brush > > > ,
977+ renders : Vec < Rc < LineRender > > ,
978+ line_ranges : Vec < Range < usize > > ,
979+ line_diffs : Vec < LineDiff > ,
980+ ghosts : Vec < Vec < Ghost > > ,
981+ ghost_height : Vec < f32 > ,
982+ quote_bars : Vec < Vec < f32 > > ,
983+ image_blocks : Vec < Option < ImageBlock > > ,
984+ inline_draws : Vec < Vec < InlineImageDraw > > ,
985+ table_lines : Vec < Option < ( Rc < TableLayout > , RowKind ) > > ,
986+ empty_layout : Rc < parley:: Layout < Brush > > ,
987+ empty_render : Rc < LineRender > ,
988+ }
989+
990+ impl Bands {
991+ fn new (
992+ n : usize ,
993+ empty_layout : Rc < parley:: Layout < Brush > > ,
994+ empty_render : Rc < LineRender > ,
995+ ) -> Self {
996+ Bands {
997+ heights : Vec :: with_capacity ( n) ,
998+ layouts : Vec :: with_capacity ( n) ,
999+ renders : Vec :: with_capacity ( n) ,
1000+ line_ranges : Vec :: with_capacity ( n) ,
1001+ line_diffs : Vec :: with_capacity ( n) ,
1002+ ghosts : Vec :: with_capacity ( n) ,
1003+ ghost_height : Vec :: with_capacity ( n) ,
1004+ quote_bars : Vec :: with_capacity ( n) ,
1005+ image_blocks : Vec :: with_capacity ( n) ,
1006+ inline_draws : Vec :: with_capacity ( n) ,
1007+ table_lines : Vec :: with_capacity ( n) ,
1008+ empty_layout,
1009+ empty_render,
1010+ }
1011+ }
1012+
1013+ /// A non-materialized row: a hidden line (`height` 0, `image_block` None) or an
1014+ /// image-block anchor (its block height + `Some(block)`). Everything else empty.
1015+ fn push_placeholder (
1016+ & mut self ,
1017+ height : f32 ,
1018+ range : Range < usize > ,
1019+ image_block : Option < ImageBlock > ,
1020+ ) {
1021+ self . heights . push ( height) ;
1022+ self . layouts . push ( self . empty_layout . clone ( ) ) ;
1023+ self . renders . push ( self . empty_render . clone ( ) ) ;
1024+ self . line_ranges . push ( range) ;
1025+ self . line_diffs . push ( LineDiff :: default ( ) ) ;
1026+ self . ghosts . push ( Vec :: new ( ) ) ;
1027+ self . ghost_height . push ( 0.0 ) ;
1028+ self . quote_bars . push ( Vec :: new ( ) ) ;
1029+ self . image_blocks . push ( image_block) ;
1030+ self . inline_draws . push ( Vec :: new ( ) ) ;
1031+ self . table_lines . push ( None ) ;
1032+ }
1033+ }
1034+
9701035pub struct DocLayout {
9711036 layouts : Vec < Rc < parley:: Layout < Brush > > > ,
9721037 /// Per-line render results (shared with the RenderCache via `Rc`), parallel to
@@ -1171,16 +1236,7 @@ impl DocLayout {
11711236 // Scan mermaid fences once, not per line (a per-line info-string check allocates).
11721237 #[ cfg( feature = "mermaid" ) ]
11731238 let mermaid_blocks = snapshot. mermaid_blocks ( ) ;
1174- let mut layouts = Vec :: with_capacity ( n) ;
1175- let mut renders = Vec :: with_capacity ( n) ;
1176- let mut line_ranges = Vec :: with_capacity ( n) ;
1177- let mut line_diffs = Vec :: with_capacity ( n) ;
1178- let mut ghosts = Vec :: with_capacity ( n) ;
1179- let mut ghost_height = Vec :: with_capacity ( n) ;
1180- let mut quote_bars: Vec < Vec < f32 > > = Vec :: with_capacity ( n) ;
1181- let mut image_blocks: Vec < Option < ImageBlock > > = Vec :: with_capacity ( n) ;
1182- let mut inline_draws: Vec < Vec < InlineImageDraw > > = Vec :: with_capacity ( n) ;
1183- let mut table_lines: Vec < Option < ( Rc < TableLayout > , RowKind ) > > = Vec :: with_capacity ( n) ;
1239+ let mut bands = Bands :: new ( n, empty_layout. clone ( ) , empty_render. clone ( ) ) ;
11841240 let mut image_urls: Vec < String > = Vec :: new ( ) ;
11851241 #[ cfg( feature = "mermaid" ) ]
11861242 let mut mermaid_sources: Vec < ( String , String ) > = Vec :: new ( ) ;
@@ -1199,7 +1255,6 @@ impl DocLayout {
11991255 let content_w = max_advance;
12001256 let img_vpad = IMG_VPAD * scale;
12011257 // Each line's total height = its ghost block above + the real line.
1202- let mut heights = Vec :: with_capacity ( n) ;
12031258 // Cursor component of a line's render key. Table rows share block-reveal state
12041259 // (all flip together when the cursor enters/leaves the block) — except the row
12051260 // the cursor actually sits on, which reveals its own markers. Three distinct
@@ -1231,17 +1286,7 @@ impl DocLayout {
12311286 fold_idx += 1 ;
12321287 }
12331288 if fold_idx < folds. len ( ) && folds[ fold_idx] . start <= i {
1234- heights. push ( 0.0 ) ;
1235- layouts. push ( empty_layout. clone ( ) ) ;
1236- renders. push ( empty_render. clone ( ) ) ;
1237- line_ranges. push ( snapshot. line_byte_range ( i) ) ;
1238- line_diffs. push ( LineDiff :: default ( ) ) ;
1239- ghosts. push ( Vec :: new ( ) ) ;
1240- ghost_height. push ( 0.0 ) ;
1241- quote_bars. push ( Vec :: new ( ) ) ;
1242- image_blocks. push ( None ) ;
1243- inline_draws. push ( Vec :: new ( ) ) ;
1244- table_lines. push ( None ) ;
1289+ bands. push_placeholder ( 0.0 , snapshot. line_byte_range ( i) , None ) ;
12451290 continue ;
12461291 }
12471292 // Mermaid fence (diagram mode = caret outside the fence). Its non-anchor lines
@@ -1263,17 +1308,7 @@ impl DocLayout {
12631308 if let Some ( m) = & mermaid
12641309 && i != m. anchor_line
12651310 {
1266- heights. push ( 0.0 ) ;
1267- layouts. push ( empty_layout. clone ( ) ) ;
1268- renders. push ( empty_render. clone ( ) ) ;
1269- line_ranges. push ( snapshot. line_byte_range ( i) ) ;
1270- line_diffs. push ( LineDiff :: default ( ) ) ;
1271- ghosts. push ( Vec :: new ( ) ) ;
1272- ghost_height. push ( 0.0 ) ;
1273- quote_bars. push ( Vec :: new ( ) ) ;
1274- image_blocks. push ( None ) ;
1275- inline_draws. push ( Vec :: new ( ) ) ;
1276- table_lines. push ( None ) ;
1311+ bands. push_placeholder ( 0.0 , snapshot. line_byte_range ( i) , None ) ;
12771312 continue ;
12781313 }
12791314 // Display-math block ($$…$$): same as mermaid — rendered mode when the caret
@@ -1294,17 +1329,7 @@ impl DocLayout {
12941329 if let Some ( m) = & math_block
12951330 && i != m. anchor_line
12961331 {
1297- heights. push ( 0.0 ) ;
1298- layouts. push ( empty_layout. clone ( ) ) ;
1299- renders. push ( empty_render. clone ( ) ) ;
1300- line_ranges. push ( snapshot. line_byte_range ( i) ) ;
1301- line_diffs. push ( LineDiff :: default ( ) ) ;
1302- ghosts. push ( Vec :: new ( ) ) ;
1303- ghost_height. push ( 0.0 ) ;
1304- quote_bars. push ( Vec :: new ( ) ) ;
1305- image_blocks. push ( None ) ;
1306- inline_draws. push ( Vec :: new ( ) ) ;
1307- table_lines. push ( None ) ;
1332+ bands. push_placeholder ( 0.0 , snapshot. line_byte_range ( i) , None ) ;
13081333 continue ;
13091334 }
13101335 // Estimate the head (above the band) and the tail (once the band has covered
@@ -1327,17 +1352,17 @@ impl DocLayout {
13271352 // estimate it (deleted-line count × row height) so the scroll extent stays
13281353 // stable instead of jumping when the line scrolls into the materialized band.
13291354 let gh = estimate_ghost_height ( diff, i, min_row) ;
1330- heights. push ( gh + h) ;
1331- layouts. push ( empty_layout. clone ( ) ) ;
1332- renders. push ( empty_render. clone ( ) ) ;
1333- line_ranges. push ( range) ;
1334- line_diffs. push ( LineDiff :: default ( ) ) ;
1335- ghosts. push ( Vec :: new ( ) ) ;
1336- ghost_height. push ( gh) ;
1337- quote_bars. push ( Vec :: new ( ) ) ;
1338- image_blocks. push ( None ) ;
1339- inline_draws. push ( Vec :: new ( ) ) ;
1340- table_lines. push ( None ) ;
1355+ bands . heights . push ( gh + h) ;
1356+ bands . layouts . push ( empty_layout. clone ( ) ) ;
1357+ bands . renders . push ( empty_render. clone ( ) ) ;
1358+ bands . line_ranges . push ( range) ;
1359+ bands . line_diffs . push ( LineDiff :: default ( ) ) ;
1360+ bands . ghosts . push ( Vec :: new ( ) ) ;
1361+ bands . ghost_height . push ( gh) ;
1362+ bands . quote_bars . push ( Vec :: new ( ) ) ;
1363+ bands . image_blocks . push ( None ) ;
1364+ bands . inline_draws . push ( Vec :: new ( ) ) ;
1365+ bands . table_lines . push ( None ) ;
13411366 continue ;
13421367 }
13431368 // Mermaid anchor (materialized): draw the diagram (or placeholder) in place of
@@ -1370,17 +1395,7 @@ impl DocLayout {
13701395 measured_count = i + 1 ;
13711396 }
13721397 }
1373- heights. push ( block_h) ;
1374- layouts. push ( empty_layout. clone ( ) ) ;
1375- renders. push ( empty_render. clone ( ) ) ;
1376- quote_bars. push ( Vec :: new ( ) ) ;
1377- line_ranges. push ( range) ;
1378- line_diffs. push ( LineDiff :: default ( ) ) ;
1379- ghosts. push ( Vec :: new ( ) ) ;
1380- ghost_height. push ( 0.0 ) ;
1381- image_blocks. push ( Some ( block) ) ;
1382- inline_draws. push ( Vec :: new ( ) ) ;
1383- table_lines. push ( None ) ;
1398+ bands. push_placeholder ( block_h, range, Some ( block) ) ;
13841399 continue ;
13851400 }
13861401 // Display-math anchor (materialized): render the math and draw it centered in
@@ -1419,17 +1434,7 @@ impl DocLayout {
14191434 measured_count = i + 1 ;
14201435 }
14211436 }
1422- heights. push ( block_h) ;
1423- layouts. push ( empty_layout. clone ( ) ) ;
1424- renders. push ( empty_render. clone ( ) ) ;
1425- quote_bars. push ( Vec :: new ( ) ) ;
1426- line_ranges. push ( range) ;
1427- line_diffs. push ( LineDiff :: default ( ) ) ;
1428- ghosts. push ( Vec :: new ( ) ) ;
1429- ghost_height. push ( 0.0 ) ;
1430- image_blocks. push ( Some ( block) ) ;
1431- inline_draws. push ( Vec :: new ( ) ) ;
1432- table_lines. push ( None ) ;
1437+ bands. push_placeholder ( block_h, range, Some ( block) ) ;
14331438 continue ;
14341439 }
14351440 // Ghost (deleted) lines rendered before this line, from the HEAD snapshot.
@@ -1688,17 +1693,17 @@ impl DocLayout {
16881693 measured_count = i + 1 ;
16891694 }
16901695 }
1691- heights. push ( total_h) ;
1692- layouts. push ( layout) ;
1693- renders. push ( lr) ;
1694- quote_bars. push ( bars) ;
1695- line_ranges. push ( range) ;
1696- line_diffs. push ( line_diff) ;
1697- ghosts. push ( line_ghosts) ;
1698- ghost_height. push ( gh) ;
1699- image_blocks. push ( image_block) ;
1700- inline_draws. push ( line_inline_draws) ;
1701- table_lines. push ( table_line) ;
1696+ bands . heights . push ( total_h) ;
1697+ bands . layouts . push ( layout) ;
1698+ bands . renders . push ( lr) ;
1699+ bands . quote_bars . push ( bars) ;
1700+ bands . line_ranges . push ( range) ;
1701+ bands . line_diffs . push ( line_diff) ;
1702+ bands . ghosts . push ( line_ghosts) ;
1703+ bands . ghost_height . push ( gh) ;
1704+ bands . image_blocks . push ( image_block) ;
1705+ bands . inline_draws . push ( line_inline_draws) ;
1706+ bands . table_lines . push ( table_line) ;
17021707 }
17031708 cache. sweep ( ) ;
17041709 render_cache. sweep ( ) ;
@@ -1716,6 +1721,20 @@ impl DocLayout {
17161721 deleted_bg : peniko_color_alpha ( theme. red , 0.15 ) ,
17171722 deleted_inline : peniko_color_alpha ( theme. red , 0.40 ) ,
17181723 } ;
1724+ let Bands {
1725+ heights,
1726+ layouts,
1727+ renders,
1728+ line_ranges,
1729+ line_diffs,
1730+ ghosts,
1731+ ghost_height,
1732+ quote_bars,
1733+ image_blocks,
1734+ inline_draws,
1735+ table_lines,
1736+ ..
1737+ } = bands;
17191738 Self {
17201739 layouts,
17211740 renders,
0 commit comments