1use crate::{Cell, Grid, Style};
2
3pub fn grid_to_ansi(grid: &Grid) -> String {
9 let mut out = String::new();
10 let mut current = Style::plain();
11
12 for row in grid.rows() {
13 emit_row(&mut out, row, &mut current);
14 out.push_str("\x1b[0m");
16 current = Style::plain();
17 out.push('\n');
18 }
19
20 out
21}
22
23fn emit_row(out: &mut String, row: &[Cell], current: &mut Style) {
24 let mut cur = *current;
25 for cell in row {
26 match cell {
27 Cell::Empty => {
28 if cur != Style::plain() {
31 out.push_str("\x1b[0m");
32 cur = Style::plain();
33 }
34 out.push(' ');
35 }
36 Cell::Continuation => {
37 }
39 Cell::Glyph { grapheme, style } => {
40 if *style != cur {
41 emit_style(out, *style);
42 cur = *style;
43 }
44 out.push_str(grapheme);
45 }
46 }
47 }
48 *current = cur;
49}
50
51fn emit_style(out: &mut String, style: Style) {
52 out.push_str("\x1b[0m");
54
55 let mut parts: Vec<String> = Vec::new();
56 if style.dim {
57 parts.push("2".to_string());
58 }
59 if style.bold {
60 parts.push("1".to_string());
61 }
62 if style.italic {
63 parts.push("3".to_string());
64 }
65 if style.underline {
66 parts.push("4".to_string());
67 }
68 if style.blink {
69 parts.push("5".to_string());
70 }
71 if style.inverse {
72 parts.push("7".to_string());
73 }
74 if style.strike {
75 parts.push("9".to_string());
76 }
77 if let Some(fg) = style.fg {
78 parts.push(format!("38;5;{}", fg));
79 }
80 if let Some(bg) = style.bg {
81 parts.push(format!("48;5;{}", bg));
82 }
83
84 if !parts.is_empty() {
85 out.push_str("\x1b[");
86 out.push_str(&parts.join(";"));
87 out.push('m');
88 }
89}