1use crate::Style;
2use unicode_width::UnicodeWidthChar;
3
4#[derive(Clone, Debug, PartialEq, Eq)]
5pub struct HistoryRun {
6 pub text: String,
7 pub style: Style,
8}
9
10#[derive(Clone, Debug, PartialEq, Eq)]
11pub struct HistoryLine {
12 pub runs: Vec<HistoryRun>,
13}
14
15#[derive(Clone, Debug, PartialEq, Eq)]
16pub struct HistoryBlock {
17 pub lines: Vec<HistoryLine>,
18}
19
20#[derive(Clone, Debug, PartialEq, Eq)]
21pub enum HistoryEntry {
22 Text(String),
23 Block(HistoryBlock),
24}
25
26pub fn history_block_from_text(content: &str, width: u16) -> HistoryBlock {
27 if width == 0 {
28 return HistoryBlock { lines: Vec::new() };
29 }
30
31 let mut lines = Vec::new();
32
33 for raw_line in content.split('\n') {
34 if raw_line.is_empty() {
35 lines.push(HistoryLine { runs: Vec::new() });
36 continue;
37 }
38
39 let mut current = String::new();
40 let mut current_width = 0u16;
41
42 for ch in raw_line.chars() {
43 let char_width = UnicodeWidthChar::width(ch).unwrap_or(0) as u16;
44 let char_width = char_width.max(1);
45 if current_width.saturating_add(char_width) > width && !current.is_empty() {
46 lines.push(HistoryLine {
47 runs: vec![HistoryRun {
48 text: std::mem::take(&mut current),
49 style: Style::default(),
50 }],
51 });
52 current_width = 0;
53 }
54
55 current.push(ch);
56 current_width = current_width.saturating_add(char_width);
57 }
58
59 lines.push(HistoryLine {
60 runs: vec![HistoryRun {
61 text: current,
62 style: Style::default(),
63 }],
64 });
65 }
66
67 if lines.is_empty() {
68 lines.push(HistoryLine { runs: Vec::new() });
69 }
70
71 HistoryBlock { lines }
72}