Skip to content

Commit

Permalink
Merge pull request #485 from zhiburt/patch/rework-span
Browse files Browse the repository at this point in the history
Rework `Span`
  • Loading branch information
zhiburt authored Jan 24, 2025
2 parents c6c4723 + 67f4356 commit 7b85b29
Show file tree
Hide file tree
Showing 10 changed files with 474 additions and 106 deletions.
10 changes: 10 additions & 0 deletions tabled/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,16 @@ name = "span"
path = "examples/span.rs"
required-features = ["std"]

[[example]]
name = "span_column"
path = "examples/span_column.rs"
required-features = ["std"]

[[example]]
name = "span_row"
path = "examples/span_row.rs"
required-features = ["std"]

[[example]]
name = "nested_table"
path = "examples/nested_table.rs"
Expand Down
58 changes: 58 additions & 0 deletions tabled/examples/span_column.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//! [COMMENTED] A list of examples for a span use.
use tabled::{
settings::{style::BorderSpanCorrection, Alignment, Span, Style},
Table,
};

fn main() {
let data = [
(1, 2, 3),
(4, 5, 6),
(7, 8, 9),
(10, 11, 12),
(13, 14, 15),
(16, 17, 18),
(19, 20, 21),
];
let mut table = Table::new(data);

table.with(Style::modern_rounded());

// Here we show how original table looks.
println!("{table}");

// Then there's a list of Span appliences and it's affects.

// Spread cell by 1 column to the right.
table.modify((0, 1), Span::column(2));

// Spread cell all the way to the right,
// Which essentially covers all row.
table.modify((1, 0), Span::column(isize::MAX));

// Spread cell by 1 column to the left.
table.modify((2, 1), Span::column(-1));

// Spread cell all the way to the left.
table.modify((3, 2), Span::column(isize::MIN));

// Spread cell to cover the whole row.
table.modify((4, 0), Span::column(0));

// Spread cell to cover the whole row.
table.modify((5, 1), Span::column(0));

// Spread cell to cover the whole row.
table.modify((6, 2), Span::column(0));

// Set a default span for a cell,
// Essentially removing a span setting for it.
table.modify((4, 0), Span::column(1));

// Correct the style to look good
table.with(BorderSpanCorrection);
table.with(Alignment::center());

println!("{table}");
}
55 changes: 55 additions & 0 deletions tabled/examples/span_row.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//! [COMMENTED] A list of examples for a span use.
use tabled::{
settings::{style::BorderSpanCorrection, Alignment, Span, Style},
Table,
};

fn main() {
let data = [
(1, 2, 3, 5, (6, 7, 8)),
(9, 10, 11, 12, (13, 14, 15)),
(16, 17, 18, 19, (20, 21, 22)),
(23, 24, 25, 26, (27, 28, 29)),
];
let mut table = Table::new(data);

table.with(Style::modern_rounded());

// Here we show how original table looks.
println!("{table}");

// Then there's a list of Span appliences and it's affects.

// Spread cell by 1 row to the right.
table.modify((0, 0), Span::row(2));

// Spread cell all the way to the right,
// Which essentially covers all columns.
table.modify((0, 1), Span::row(isize::MAX));

// Spread cell by 1 row to the top.
table.modify((1, 2), Span::row(-1));

// Spread cell all the way to the top.
table.modify((2, 3), Span::row(isize::MIN));

// Spread cell to cover the whole column.
table.modify((0, 4), Span::row(0));

// Spread cell to cover the whole column.
table.modify((1, 5), Span::row(0));

// Spread cell to cover the whole column.
table.modify((2, 6), Span::row(0));

// Set a default span for a cell,
// Essentially removing a span setting for it.
table.modify((0, 4), Span::row(1));

// Correct the style to look good
table.with(BorderSpanCorrection);
table.with(Alignment::center_vertical());

println!("{table}");
}
99 changes: 75 additions & 24 deletions tabled/src/settings/span/column.rs
Original file line number Diff line number Diff line change
@@ -1,58 +1,113 @@
use std::cmp::{self, Ordering};

use crate::{
grid::{
config::{ColoredConfig, Entity, Position, SpannedConfig},
records::{ExactRecords, Records},
records::{ExactRecords, PeekableRecords, Records, RecordsMut},
},
settings::CellOption,
};

/// Columns (Vertical) span.
/// Columns (horizontal) span.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct ColumnSpan {
size: usize,
size: isize,
}

impl ColumnSpan {
/// Creates a new column (vertical) span.
pub fn new(size: usize) -> Self {
/// Creates a new column (horizontal) span.
pub fn new(size: isize) -> Self {
Self { size }
}

/// Creates a new column (vertical) span with a maximux value possible.
/// Creates a new column (horizontal) span with a maximux value possible.
pub fn max() -> Self {
Self::new(usize::MAX)
Self::new(isize::MAX)
}

/// Creates a new column (horizontal) span with a min value possible.
pub fn min() -> Self {
Self::new(isize::MIN)
}

/// Creates a new column (horizontal) to spread all the columns.
pub fn spread() -> Self {
Self::new(0)
}
}

impl<R> CellOption<R, ColoredConfig> for ColumnSpan
where
R: Records + ExactRecords,
R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
{
fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
let count_rows = records.count_rows();
let count_cols = records.count_columns();
let shape = (count_rows, count_cols).into();

for pos in entity.iter(count_rows, count_cols) {
set_col_span(records, cfg, self.size, pos, shape);
}

set_col_spans(cfg, self.size, entity, (count_rows, count_cols));
remove_false_spans(cfg);
}
}

fn set_col_spans(cfg: &mut SpannedConfig, span: usize, entity: Entity, shape: (usize, usize)) {
for pos in entity.iter(shape.0, shape.1) {
if !pos.is_covered(shape.into()) {
continue;
}
fn set_col_span<R>(
recs: &mut R,
cfg: &mut SpannedConfig,
span: isize,
pos: Position,
shape: Position,
) where
R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
{
if !pos.is_covered(shape) {
return;
}

match span.cmp(&0) {
Ordering::Less => {
// * got correct value [span, col]
// * clean the route from col to pos.col()
// * set the (pos.row(), col) to content from pos
// * set span

let span = span.unsigned_abs();
let (col, span) = if span > pos.col() {
(0, pos.col())
} else {
(pos.col() - span, span)
};

let content = recs.get_text(pos).to_string();

for i in col..span + 1 {
recs.set(Position::new(pos.row(), i), String::new());
}

let mut span = span;
if !is_column_span_valid(pos.col(), span, shape.1) {
span = shape.1 - pos.col();
recs.set(Position::new(pos.row(), col), content);
cfg.set_column_span(Position::new(pos.row(), col), span + 1);
}
Ordering::Equal => {
let content = recs.get_text(pos).to_string();
let span = recs.count_columns();

if span_has_intersections(cfg, pos, span) {
continue;
for i in 0..recs.count_columns() {
recs.set(Position::new(pos.row(), i), String::new());
}

recs.set(Position::new(pos.row(), 0), content);
cfg.set_column_span(Position::new(pos.row(), 0), span);
}
Ordering::Greater => {
let span = cmp::min(span as usize, shape.col() - pos.col());
if span_has_intersections(cfg, pos, span) {
return;
}

set_span_column(cfg, pos, span);
set_span_column(cfg, pos, span);
}
}
}

Expand Down Expand Up @@ -85,10 +140,6 @@ fn closest_visible(cfg: &SpannedConfig, mut pos: Position) -> Option<usize> {
}
}

fn is_column_span_valid(col: usize, span: usize, count_cols: usize) -> bool {
span + col <= count_cols
}

fn span_has_intersections(cfg: &SpannedConfig, p: Position, span: usize) -> bool {
for col in p.col()..p.col() + span {
if !cfg.is_cell_visible((p.row(), col).into()) {
Expand Down
58 changes: 29 additions & 29 deletions tabled/src/settings/span/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,11 @@
//!
//! # Example
//!
//! ```
//! use tabled::{settings::{Span, Modify}, Table};
//!
//! let data = [[1, 2, 3], [4, 5, 6]];
//!
//! let table = Table::new(data)
//! .with(Modify::new((2, 0)).with(Span::column(2)))
//! .with(Modify::new((0, 1)).with(Span::column(2)))
//! .to_string();
//!
//! assert_eq!(
//! table,
//! concat!(
//! "+---+---+---+\n",
//! "| 0 | 1 |\n",
//! "+---+---+---+\n",
//! "| 1 | 2 | 3 |\n",
//! "+---+---+---+\n",
//! "| 4 | 6 |\n",
//! "+---+---+---+",
//! )
//! )
//! ```rust,no_run
//! # use tabled::{Table, settings::{Style, Span, Modify, object::Columns}};
//! # let data: Vec<&'static str> = Vec::new();
//! let table = Table::new(&data)
//! .with(Modify::new(Columns::single(0)).with(Span::column(2)));
//! ```
mod column;
Expand All @@ -40,11 +23,28 @@ pub use row::RowSpan;
/// - size is bigger then the total number of columns.
/// - size is bigger then the total number of rows.
///
/// ```rust,no_run
/// # use tabled::{Table, settings::{Style, Span, Modify, object::Columns}};
/// # let data: Vec<&'static str> = Vec::new();
/// let table = Table::new(&data)
/// .with(Modify::new(Columns::single(0)).with(Span::column(2)));
/// ```
/// use tabled::{settings::{Span, Modify}, Table};
///
/// let data = [[1, 2, 3], [4, 5, 6]];
///
/// let table = Table::new(data)
/// .with(Modify::new((2, 0)).with(Span::column(2)))
/// .with(Modify::new((0, 1)).with(Span::column(2)))
/// .to_string();
///
/// assert_eq!(
/// table,
/// concat!(
/// "+---+---+---+\n",
/// "| 0 | 1 |\n",
/// "+---+---+---+\n",
/// "| 1 | 2 | 3 |\n",
/// "+---+---+---+\n",
/// "| 4 | 6 |\n",
/// "+---+---+---+",
/// )
/// )
/// ```
///
/// [`Table`]: crate::Table
Expand All @@ -55,14 +55,14 @@ impl Span {
/// New constructs a horizontal/column [`Span`].
///
/// If size is bigger then the total number of columns it will be ignored.
pub fn column(size: usize) -> ColumnSpan {
pub fn column(size: isize) -> ColumnSpan {
ColumnSpan::new(size)
}

/// New constructs a vertical/row [`Span`].
///
/// If size is bigger then the total number of rows it will be ignored.
pub fn row(size: usize) -> RowSpan {
pub fn row(size: isize) -> RowSpan {
RowSpan::new(size)
}
}
Loading

0 comments on commit 7b85b29

Please sign in to comment.