Repository: zhiburt/tabled (rust)

Issue:
colided `span`ed columns cause incorrect rendering
It doesn't affect `tabled` but it must be resolved to have the same algorithm for `tabled::Panel` in #77.
*Actually not for #77 but for exposure of #71 certainly

The correct view is not defined yet.
But the below test shows an issue.

```rust

#[test]
fn render_2_colided_row_span_3x3() {
    let mut grid = Grid::new(3, 3);
    grid.set_cell_borders(DEFAULT_CELL_STYLE.clone());

    grid.set(&Entity::Cell(0, 0), Settings::new().text("0-01111111").span(2));
    grid.set(&Entity::Cell(1, 0), Settings::new().text("1-0"));
    grid.set(&Entity::Cell(1, 1), Settings::new().text("1-1").span(2));
    grid.set(&Entity::Cell(1, 2), Settings::new().text("1-1").span(0));
    grid.set(&Entity::Cell(2, 0), Settings::new().text("2-0"));
    grid.set(&Entity::Cell(2, 1), Settings::new().text("2-1"));
    grid.set(&Entity::Cell(2, 2), Settings::new().text("2-2"));

    let grid = grid.to_string();

    let expected = concat!(
        "+---+------+---+\n",
        "|0-01111111|   |\n",
        "+---+------+---+\n",
        "|1-0|1-1       |\n",
        "+---+------+---+\n",
        "|2-0|  2-1 |2-2|\n",
        "+---+------+---+\n",
    );

    println!("{}", grid);

    assert_eq!(grid, expected);

    let mut grid = Grid::new(3, 3);
    grid.set_cell_borders(DEFAULT_CELL_STYLE.clone());

    grid.set(&Entity::Cell(0, 0), Settings::new().text("0-0").span(2));
    grid.set(&Entity::Cell(1, 0), Settings::new().text("1-0"));
    grid.set(&Entity::Cell(1, 1), Settings::new().text("1-11111111").span(2));
    grid.set(&Entity::Cell(1, 2), Settings::new().text("1-1").span(0));
    grid.set(&Entity::Cell(2, 0), Settings::new().text("2-0"));
    grid.set(&Entity::Cell(2, 1), Settings::new().text("2-1"));
    grid.set(&Entity::Cell(2, 2), Settings::new().text("2-2"));

    let grid = grid.to_string();


    let expected = concat!(
        "+---+------+---+\n",
        "|0-0       |   |\n",
        "+---+------+---+\n",
        "|1-0|1-11111111|\n",
        "+---+------+---+\n",
        "|2-0|  2-1 |2-2|\n",
        "+---+------+---+\n",
    );


    println!("{}", grid);

    assert_eq!(grid, expected);
}
```

Pull request:
Fix logic of span rendering
closes #78 

Patch:
diff --git a/papergrid/src/lib.rs b/papergrid/src/lib.rs
index 832b4d4..eeefc3a 100644
--- a/papergrid/src/lib.rs
+++ b/papergrid/src/lib.rs
@@ -1143,6 +1143,8 @@ fn columns_width(
             spans.insert(style.span);
         })
     });
+    spans.remove(&0);
+
     spans.into_iter().for_each(|span| {
         adjust_width(
             &mut widths,
@@ -1151,7 +1153,7 @@ fn columns_width(
             count_rows,
             count_columns,
             span,
-        );
+        )
     });
 
     widths
@@ -1165,15 +1167,22 @@ fn adjust_width(
     count_columns: usize,
     span: usize,
 ) {
-    for column in 0..count_columns {
-        let start = column;
-        let end = column + span;
-        if end > count_columns {
-            break;
-        }
+    let ranges = (0..count_columns)
+        .map(|col| (col, col + span))
+        .take_while(|&(_, end)| end <= count_columns);
 
+    for (start, end) in ranges.clone() {
         adjust_range_width(widths, styles, borders, count_rows, start, end);
     }
+
+    // sometimes the adjustment of later stages affect the adjastement of privious stages.
+    // therefore we check if this is the case and re run the adjustement one more time.
+    for (start, end) in ranges {
+        let is_range_complete = is_range_complete(styles, widths, borders, count_rows, start, end);
+        if !is_range_complete {
+            adjust_range_width(widths, styles, borders, count_rows, start, end);
+        }
+    }
 }
 
 fn adjust_range_width(
@@ -1189,16 +1198,8 @@ fn adjust_range_width(
     }
     let span = end_column - start_column;
 
-    let (row_with_max_width, max_width) = (0..count_rows)
-        .filter(|&row| is_row_consistent(&styles[row][start_column..end_column]))
-        .filter(|&row| {
-            let row_spans = (start_column..end_column)
-                .filter(|&column| is_cell_visible(&styles[row], column))
-                .map(|col| styles[row][col].span)
-                .sum::<usize>();
-
-            row_spans <= span
-        })
+    // find max width of a column range
+    let (max_row, max_width) = (0..count_rows)
         .map(|row| {
             let width = row_width(
                 &styles[row],
@@ -1206,7 +1207,6 @@ fn adjust_range_width(
                 &borders[row],
                 start_column,
                 end_column,
-                true,
             );
             (row, width)
         })
@@ -1218,50 +1218,121 @@ fn adjust_range_width(
         return;
     }
 
+    // increase the widths
     (0..count_rows)
-        .filter(|&row| row != row_with_max_width)
-        .filter(|&row| is_row_consistent(&styles[row][start_column..end_column]))
-        .filter(|&row| {
-            let row_spans = (start_column..end_column)
-                .filter(|&column| is_cell_visible(&styles[row], column))
-                .map(|col| styles[row][col].span)
-                .sum::<usize>();
-
-            row_spans <= span
-        })
+        .filter(|&row| row != max_row)
+        .filter(|&row| !is_row_bigger_than_span(&styles[row], span))
+        .filter(|&row| !is_there_out_of_scope_cell(&styles[row], start_column, end_column)) // ignore the cell we do handle this case later on
         .for_each(|row| {
-            inc_width_to_cells(
-                &mut widths[row],
+            let row_width = row_width(
                 &styles[row],
+                &widths[row],
                 &borders[row],
                 start_column,
                 end_column,
-                max_width,
+            );
+
+            let diff = max_width - row_width;
+
+            inc_cells_width(
+                &mut widths[row],
+                &styles[row],
+                start_column,
+                end_column,
+                diff,
             );
         });
+
+    // fixing the rows with out_of_scope cells
+    //
+    // these cells may not have correct width, therefore
+    // we replace these cells's width with
+    // a width of cells with the same span and on the same column.
+    (0..count_rows)
+        .filter(|&row| row != max_row)
+        .filter(|&row| !is_row_bigger_than_span(&styles[row], span))
+        .filter(|&row| is_there_out_of_scope_cell(&styles[row], start_column, end_column))
+        .for_each(|row| {
+            (start_column..end_column)
+                .filter(|&col| is_cell_visible(&styles[row], col))
+                .for_each(|col| {
+                    let cell_with_the_same_cell = (0..count_rows)
+                        .filter(|&r| r != max_row)
+                        .filter(|&r| r != row)
+                        .filter(|&r| !is_row_bigger_than_span(&styles[r], span))
+                        .filter(|&r| {
+                            !is_there_out_of_scope_cell(&styles[r], start_column, end_column)
+                        })
+                        .find(|&r| styles[r][col].span == styles[row][col].span);
+
+                    if let Some(r) = cell_with_the_same_cell {
+                        widths[row][col] = widths[r][col];
+                    }
+                })
+        });
 }
 
-fn is_row_consistent(styles: &[Style]) -> bool {
-    if styles.is_empty() {
-        return true;
-    }
+fn is_there_out_of_scope_cell(styles: &[Style], start_column: usize, end_column: usize) -> bool {
+    let first_cell_is_invisible = !is_cell_visible(styles, start_column);
+    let any_cell_out_of_scope = (start_column..end_column)
+        .filter(|&col| is_cell_visible(styles, col))
+        .any(|col| !is_cell_in_scope(styles, col, end_column));
 
-    if styles[0].span == 0 {
-        return false;
-    }
+    first_cell_is_invisible || any_cell_out_of_scope
+}
 
-    styles
-        .iter()
-        .zip(styles.len()..)
-        .all(|(style, max_span)| style.span <= max_span)
+fn is_cell_in_scope(styles: &[Style], col: usize, end_col: usize) -> bool {
+    styles[col].span + col <= end_col
+}
+
+fn is_row_bigger_than_span(styles: &[Style], span: usize) -> bool {
+    styles[0].span > span
 }
 
 fn is_cell_visible(row_styles: &[Style], column: usize) -> bool {
-    row_styles[column].span != 0
-        && !row_styles[..column]
-            .iter()
-            .zip(column..)
-            .any(|(style, span)| style.span > span)
+    let is_span_zero = row_styles[column].span == 0;
+    let is_cell_overriden = row_styles[..column]
+        .iter()
+        .enumerate()
+        .any(|(col, style)| style.span > column - col);
+
+    !is_span_zero && !is_cell_overriden
+}
+
+fn is_range_complete(
+    styles: &[Vec<Style>],
+    widths: &[Vec<usize>],
+    borders: &[Vec<BorderLine>],
+    count_rows: usize,
+    start_column: usize,
+    end_column: usize,
+) -> bool {
+    let is_not_complete = (0..count_rows)
+        .filter(|&row| !is_there_out_of_scope_cell(&styles[row], start_column, end_column))
+        .map(|row| {
+            row_width(
+                &styles[row],
+                &widths[row],
+                &borders[row],
+                start_column,
+                end_column,
+            )
+        })
+        .fold(None, |mut acc, width| {
+            match acc {
+                Some((w, true)) if w != width => {
+                    acc = Some((0, false));
+                }
+                None => {
+                    acc = Some((width, true));
+                }
+                _ => {}
+            };
+
+            acc
+        });
+
+    matches!(is_not_complete, Some((_, true)))
 }
 
 fn row_width(
@@ -1270,51 +1341,40 @@ fn row_width(
     borders: &[BorderLine],
     column_start: usize,
     column_end: usize,
-    consider_borders: bool,
 ) -> usize {
     let width = (column_start..column_end)
         .filter(|&i| is_cell_visible(styles, i))
+        .filter(|&i| is_cell_in_scope(styles, i, column_end))
         .map(|i| widths[i])
         .sum::<usize>();
 
-    if width == 0 {
-        return 0;
-    }
-
-    let border_count = if consider_borders {
-        if column_end - column_start == 0 {
-            0
-        } else {
-            (column_start..column_end)
-                .filter(|&i| is_cell_visible(styles, i))
-                .filter(|&i| {
-                    if i == column_start {
-                        false
-                    } else {
-                        borders[i].connector1.is_some()
-                    }
-                })
-                .count()
-        }
-    } else {
+    let border_count = if column_end - column_start == 0 {
         0
+    } else {
+        (column_start..column_end)
+            .filter(|&i| is_cell_visible(styles, i))
+            .filter(|&i| is_cell_in_scope(styles, i, column_end))
+            .filter(|&i| {
+                if i == column_start {
+                    false
+                } else {
+                    borders[i].connector1.is_some()
+                }
+            })
+            .count()
     };
 
     width + border_count
 }
 
-fn inc_width_to_cells(
+fn inc_cells_width(
     widths: &mut [usize],
     styles: &[Style],
-    borders: &[BorderLine],
     start_range: usize,
     end_range: usize,
-    width: usize,
+    inc: usize,
 ) {
-    let a = row_width(styles, widths, borders, start_range, end_range, true);
-    let diff = width - a;
-
-    (0..diff)
+    (0..inc)
         .zip(
             (start_range..end_range)
                 .filter(|&i| is_cell_visible(styles, i))
diff --git a/src/width.rs b/src/width.rs
index 9b8a933..3756e0f 100644
--- a/src/width.rs
+++ b/src/width.rs
@@ -334,7 +334,7 @@ fn chunks(s: &str, width: usize) -> Vec<String> {
 ///
 /// let table = Table::new(&data)
 ///     .with(Style::github_markdown())
-///     .with(Modify::new(Full).with(MinWidth(10)));
+///     .with(Modify::new(Full).with(MinWidth::new(10)));
 /// ```
 pub struct MinWidth {
     size: usize,


Test patch:
diff --git a/papergrid/tests/render.rs b/papergrid/tests/render.rs
index 64ccc55..8b7f0b8 100644
--- a/papergrid/tests/render.rs
+++ b/papergrid/tests/render.rs
@@ -359,6 +359,152 @@ fn render_row_span_3x3_with_horizontal_ident() {
     assert_eq!(grid, expected);
 }
 
+#[test]
+fn render_2_colided_row_span_3x3() {
+    let mut grid = Grid::new(3, 3);
+    grid.set_cell_borders(DEFAULT_CELL_STYLE.clone());
+
+    grid.set(
+        &Entity::Cell(0, 0),
+        Settings::new().text("0-0xxxxxxx").span(2),
+    );
+    grid.set(&Entity::Cell(0, 2), Settings::new().text("0-2"));
+    grid.set(&Entity::Cell(1, 0), Settings::new().text("1-0"));
+    grid.set(&Entity::Cell(1, 1), Settings::new().text("1-1").span(2));
+    grid.set(&Entity::Cell(2, 0), Settings::new().text("2-0"));
+    grid.set(&Entity::Cell(2, 1), Settings::new().text("2-1"));
+    grid.set(&Entity::Cell(2, 2), Settings::new().text("2-2"));
+
+    let grid = grid.to_string();
+
+    let expected = concat!(
+        "+-----+----+---+\n",
+        "|0-0xxxxxxx|0-2|\n",
+        "+-----+----+---+\n",
+        "|1-0  |1-1     |\n",
+        "+-----+----+---+\n",
+        "|2-0  |2-1 |2-2|\n",
+        "+-----+----+---+\n",
+    );
+
+    assert_eq!(grid, expected);
+
+    let mut grid = Grid::new(3, 3);
+    grid.set_cell_borders(DEFAULT_CELL_STYLE.clone());
+
+    grid.set(&Entity::Cell(0, 0), Settings::new().text("0-0").span(2));
+    grid.set(&Entity::Cell(0, 2), Settings::new().text("0-2"));
+    grid.set(&Entity::Cell(1, 0), Settings::new().text("1-0"));
+    grid.set(
+        &Entity::Cell(1, 1),
+        Settings::new().text("1-1xxxxxxx").span(2),
+    );
+    grid.set(&Entity::Cell(2, 0), Settings::new().text("2-0"));
+    grid.set(&Entity::Cell(2, 1), Settings::new().text("2-1"));
+    grid.set(&Entity::Cell(2, 2), Settings::new().text("2-2"));
+
+    let grid = grid.to_string();
+
+    let expected = concat!(
+        "+---+-----+----+\n",
+        "|0-0      |0-2 |\n",
+        "+---+-----+----+\n",
+        "|1-0|1-1xxxxxxx|\n",
+        "+---+-----+----+\n",
+        "|2-0|2-1  |2-2 |\n",
+        "+---+-----+----+\n",
+    );
+
+    assert_eq!(grid, expected);
+
+    let mut grid = Grid::new(3, 3);
+    grid.set_cell_borders(DEFAULT_CELL_STYLE.clone());
+
+    grid.set(&Entity::Cell(0, 0), Settings::new().text("0-0").span(2));
+    grid.set(&Entity::Cell(0, 2), Settings::new().text("0-2"));
+    grid.set(&Entity::Cell(1, 0), Settings::new().text("1-0"));
+    grid.set(
+        &Entity::Cell(1, 1),
+        Settings::new().text("1-1xxxxxxx").span(2),
+    );
+    grid.set(
+        &Entity::Cell(2, 0),
+        Settings::new().text("2-0xxxxxxxxxxxxx"),
+    );
+    grid.set(&Entity::Cell(2, 1), Settings::new().text("2-1"));
+    grid.set(&Entity::Cell(2, 2), Settings::new().text("2-2"));
+
+    let grid = grid.to_string();
+
+    let expected = concat!(
+        "+----------------+-----+----+\n",
+        "|0-0                   |0-2 |\n",
+        "+----------------+-----+----+\n",
+        "|1-0             |1-1xxxxxxx|\n",
+        "+----------------+-----+----+\n",
+        "|2-0xxxxxxxxxxxxx|2-1  |2-2 |\n",
+        "+----------------+-----+----+\n",
+    );
+
+    assert_eq!(grid, expected);
+
+    let mut grid = Grid::new(3, 3);
+    grid.set_cell_borders(DEFAULT_CELL_STYLE.clone());
+
+    grid.set(&Entity::Cell(0, 0), Settings::new().text("0-0").span(2));
+    grid.set(&Entity::Cell(0, 2), Settings::new().text("0-2"));
+    grid.set(&Entity::Cell(1, 0), Settings::new().text("1-0"));
+    grid.set(&Entity::Cell(1, 1), Settings::new().text("1-1").span(2));
+    grid.set(&Entity::Cell(2, 0), Settings::new().text("2-0"));
+    grid.set(
+        &Entity::Cell(2, 1),
+        Settings::new().text("2-1xxxxxxxxxxxxx"),
+    );
+    grid.set(&Entity::Cell(2, 2), Settings::new().text("2-2"));
+
+    let grid = grid.to_string();
+
+    let expected = concat!(
+        "+---+----------------+---+\n",
+        "|0-0                 |0-2|\n",
+        "+---+----------------+---+\n",
+        "|1-0|1-1                 |\n",
+        "+---+----------------+---+\n",
+        "|2-0|2-1xxxxxxxxxxxxx|2-2|\n",
+        "+---+----------------+---+\n",
+    );
+
+    assert_eq!(grid, expected);
+
+    let mut grid = Grid::new(3, 3);
+    grid.set_cell_borders(DEFAULT_CELL_STYLE.clone());
+
+    grid.set(&Entity::Cell(0, 0), Settings::new().text("0-0").span(2));
+    grid.set(
+        &Entity::Cell(0, 2),
+        Settings::new().text("0-2xxxxxxxxxxxxx"),
+    );
+    grid.set(&Entity::Cell(1, 0), Settings::new().text("1-0"));
+    grid.set(&Entity::Cell(1, 1), Settings::new().text("1-1").span(2));
+    grid.set(&Entity::Cell(2, 0), Settings::new().text("2-0"));
+    grid.set(&Entity::Cell(2, 1), Settings::new().text("2-1"));
+    grid.set(&Entity::Cell(2, 2), Settings::new().text("2-2"));
+
+    let grid = grid.to_string();
+
+    let expected = concat!(
+        "+---+---+----------------+\n",
+        "|0-0    |0-2xxxxxxxxxxxxx|\n",
+        "+---+---+----------------+\n",
+        "|1-0|1-1                 |\n",
+        "+---+---+----------------+\n",
+        "|2-0|2-1|2-2             |\n",
+        "+---+---+----------------+\n",
+    );
+
+    assert_eq!(grid, expected);
+}
+
 #[test]
 fn render_row_span_with_different_length() {
     let mut grid = Grid::new(3, 2);