tabled/settings/height/
table_height_limit.rs

1use crate::{
2    grid::{
3        config::{ColoredConfig, Entity},
4        dimension::CompleteDimension,
5        records::{ExactRecords, IntoRecords, PeekableRecords, Records, RecordsMut},
6        util::string::{count_lines, get_lines},
7    },
8    settings::{
9        measurement::Measurement,
10        peaker::{Peaker, PriorityNone},
11        Height, TableOption,
12    },
13};
14
15use super::util::get_table_height;
16
17/// A modification of a table to decrease the table height.
18#[derive(Debug)]
19pub struct TableHeightLimit<W = usize, P = PriorityNone> {
20    height: W,
21    priority: P,
22}
23
24impl<W> TableHeightLimit<W, PriorityNone> {
25    /// Creates a new object.
26    pub fn new(height: W) -> Self
27    where
28        W: Measurement<Height>,
29    {
30        Self {
31            height,
32            priority: PriorityNone::default(),
33        }
34    }
35
36    /// Sets a different priority logic.
37    pub fn priority<P>(self, priority: P) -> TableHeightLimit<W, P>
38    where
39        P: Peaker,
40    {
41        TableHeightLimit {
42            priority,
43            height: self.height,
44        }
45    }
46}
47
48impl<R, W, P> TableOption<R, ColoredConfig, CompleteDimension> for TableHeightLimit<W, P>
49where
50    W: Measurement<Height>,
51    P: Peaker + Clone,
52    R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
53    for<'a> &'a R: Records,
54    for<'a> <<&'a R as Records>::Iter as IntoRecords>::Cell: AsRef<str>,
55{
56    fn change(self, records: &mut R, cfg: &mut ColoredConfig, dims: &mut CompleteDimension) {
57        let count_rows = records.count_rows();
58        let count_cols = records.count_columns();
59
60        if count_rows == 0 || count_cols == 0 {
61            return;
62        }
63
64        let height = self.height.measure(&*records, cfg);
65        let (total, mut heights) = get_table_height(&*records, cfg);
66        if total <= height {
67            return;
68        }
69
70        decrease_list(&mut heights, total, height, self.priority);
71
72        for (row, &height) in heights.iter().enumerate() {
73            for col in 0..count_cols {
74                let text = records.get_text((row, col).into());
75                let count_lines = count_lines(text);
76
77                if count_lines <= height {
78                    continue;
79                }
80
81                let text = limit_lines(text, height);
82
83                records.set((row, col).into(), text);
84            }
85        }
86
87        dims.set_heights(heights);
88    }
89
90    fn hint_change(&self) -> Option<Entity> {
91        // NOTE: we set correct heights but we don't change widths
92        //       so it must be normal to not have recalculations
93        None
94    }
95}
96
97fn decrease_list<P>(list: &mut [usize], total: usize, mut value: usize, mut peaker: P)
98where
99    P: Peaker,
100{
101    while value != total {
102        let p = peaker.peak(&[], list);
103        let row = match p {
104            Some(row) => row,
105            None => break,
106        };
107
108        list[row] -= 1;
109        value += 1;
110    }
111}
112
113fn limit_lines(s: &str, n: usize) -> String {
114    let mut text = String::new();
115    for (i, line) in get_lines(s).take(n).enumerate() {
116        if i > 0 {
117            text.push('\n');
118        }
119
120        text.push_str(&line);
121    }
122
123    text
124}