@@ -14,6 +14,7 @@ use serde::Serialize;
1414
1515use super :: column:: Column ;
1616use super :: column:: ColumnsExt ;
17+ use super :: output:: DEFAULT_MIN_ROW_HEIGHT ;
1718use super :: output:: OutputRendererBuilder ;
1819
1920pub trait Renderer < N > {
@@ -22,6 +23,13 @@ pub trait Renderer<N> {
2223 // Returns the width of the graph line, possibly including another node.
2324 fn width ( & self , new_node : Option < & N > , new_parents : Option < & Vec < Ancestor < N > > > ) -> u64 ;
2425
26+ // Set the minimum rendered row height.
27+ fn set_min_row_height ( & mut self , _min_row_height : usize ) { }
28+
29+ // Set whether disconnected consecutive nodes should be staggered into
30+ // different columns instead of separated by a blank line.
31+ fn set_stagger_disconnected_nodes ( & mut self , _stagger : bool ) { }
32+
2533 // Reserve a column for the given node.
2634 fn reserve ( & mut self , node : N ) ;
2735
@@ -40,6 +48,15 @@ pub trait Renderer<N> {
4048/// Converts a sequence of DAG node descriptions into rendered graph rows.
4149pub struct GraphRowRenderer < N > {
4250 columns : Vec < Column < N > > ,
51+ // The remaining fields only have an effect when min_row_height is 1. With
52+ // taller rows, the padding row already distinguishes connected same-column
53+ // nodes from disconnected same-column nodes. But when there is no padding
54+ // row, we must track what column the previous node is in so that either
55+ // vertical or horizontal space can be added to indicate that the next node
56+ // is not connected to the previous node.
57+ min_row_height : usize ,
58+ stagger_disconnected_nodes : bool ,
59+ previous_node_column : Option < usize > ,
4360}
4461
4562/// Ancestor type indication for an ancestor or parent node.
@@ -298,6 +315,9 @@ pub struct GraphRow<N> {
298315
299316 /// The pad columns for this row.
300317 pub pad_lines : Vec < PadLine > ,
318+
319+ /// True if a blank line should be rendered before this row.
320+ pub blank_line_before : bool ,
301321}
302322
303323impl < N > GraphRowRenderer < N >
@@ -308,6 +328,9 @@ where
308328 pub fn new ( ) -> Self {
309329 GraphRowRenderer {
310330 columns : Vec :: new ( ) ,
331+ min_row_height : DEFAULT_MIN_ROW_HEIGHT ,
332+ stagger_disconnected_nodes : false ,
333+ previous_node_column : None ,
311334 }
312335 }
313336
@@ -335,6 +358,20 @@ where
335358 // space for the node, then adding the new node would create
336359 // a new column.
337360 if self . columns . find ( node) . is_none ( ) {
361+ if self . min_row_height == 1 && self . stagger_disconnected_nodes {
362+ if let Some ( previous_node_column) = self . previous_node_column {
363+ if self . columns . get ( previous_node_column) == Some ( & Column :: Empty ) {
364+ // Dense stagger mode cannot use the previous node's column for an
365+ // unallocated node, so do not count that empty column as available.
366+ empty_columns = empty_columns. saturating_sub ( 1 ) ;
367+ } else if previous_node_column == self . columns . len ( ) {
368+ // The previous node's column was trimmed from the end of the column
369+ // list. To keep the new node out of that column, rendering it requires
370+ // a blank placeholder column plus a new column for the node.
371+ width += 1 ;
372+ }
373+ }
374+ }
338375 if empty_columns == 0 {
339376 width += 1 ;
340377 } else {
@@ -360,6 +397,14 @@ where
360397 width as u64
361398 }
362399
400+ fn set_min_row_height ( & mut self , min_row_height : usize ) {
401+ self . min_row_height = min_row_height;
402+ }
403+
404+ fn set_stagger_disconnected_nodes ( & mut self , stagger : bool ) {
405+ self . stagger_disconnected_nodes = stagger;
406+ }
407+
363408 fn reserve ( & mut self , node : N ) {
364409 if self . columns . find ( & node) . is_none ( ) {
365410 if let Some ( index) = self . columns . first_empty ( ) {
@@ -378,10 +423,30 @@ where
378423 message : String ,
379424 ) -> GraphRow < N > {
380425 // Find a column for this node.
381- let column = self . columns . find ( & node) . unwrap_or_else ( || {
382- self . columns
383- . first_empty ( )
384- . unwrap_or_else ( || self . columns . new_empty ( ) )
426+ let existing_column = self . columns . find ( & node) ;
427+ let mut blank_line_before = false ;
428+ let column = existing_column. unwrap_or_else ( || {
429+ let column = if self . min_row_height == 1 && self . stagger_disconnected_nodes {
430+ if let Some ( index) = self . columns . iter ( ) . enumerate ( ) . find_map ( |( index, column) | {
431+ ( * column == Column :: Empty && Some ( index) != self . previous_node_column )
432+ . then_some ( index)
433+ } ) {
434+ index
435+ } else {
436+ if self . previous_node_column == Some ( self . columns . len ( ) ) {
437+ self . columns . push ( Column :: Empty ) ;
438+ }
439+ self . columns . new_empty ( )
440+ }
441+ } else {
442+ self . columns
443+ . first_empty ( )
444+ . unwrap_or_else ( || self . columns . new_empty ( ) )
445+ } ;
446+ blank_line_before = self . min_row_height == 1
447+ && !self . stagger_disconnected_nodes
448+ && Some ( column) == self . previous_node_column ;
449+ column
385450 } ) ;
386451 self . columns [ column] = Column :: Empty ;
387452
@@ -527,6 +592,7 @@ where
527592
528593 // Now that we have assigned all the columns, reset their state.
529594 self . columns . reset ( ) ;
595+ self . previous_node_column = Some ( column) ;
530596
531597 // Filter out the link line or term line if they are not needed.
532598 let link_line = Some ( link_line) . filter ( |_| need_link_line) ;
@@ -541,6 +607,7 @@ where
541607 link_line,
542608 term_line,
543609 pad_lines,
610+ blank_line_before,
544611 }
545612 }
546613}
0 commit comments