1pub use vtcode_commons::diff_theme::{
8 DiffColorLevel, DiffTheme, diff_add_bg, diff_del_bg, diff_gutter_bg_add_light,
9 diff_gutter_bg_del_light, diff_gutter_fg_light,
10};
11pub use vtcode_commons::styling::DiffColorPalette;
12
13use ratatui::style::{Color as RatatuiColor, Modifier, Style as RatatuiStyle};
14
15fn ratatui_color_from_anstyle(color: anstyle::Color) -> RatatuiColor {
19 match color {
20 anstyle::Color::Ansi(c) => match c {
21 anstyle::AnsiColor::Black => RatatuiColor::Black,
22 anstyle::AnsiColor::Red => RatatuiColor::Red,
23 anstyle::AnsiColor::Green => RatatuiColor::Green,
24 anstyle::AnsiColor::Yellow => RatatuiColor::Yellow,
25 anstyle::AnsiColor::Blue => RatatuiColor::Blue,
26 anstyle::AnsiColor::Magenta => RatatuiColor::Magenta,
27 anstyle::AnsiColor::Cyan => RatatuiColor::Cyan,
28 anstyle::AnsiColor::White => RatatuiColor::White,
29 anstyle::AnsiColor::BrightBlack => RatatuiColor::DarkGray,
30 anstyle::AnsiColor::BrightRed => RatatuiColor::LightRed,
31 anstyle::AnsiColor::BrightGreen => RatatuiColor::LightGreen,
32 anstyle::AnsiColor::BrightYellow => RatatuiColor::LightYellow,
33 anstyle::AnsiColor::BrightBlue => RatatuiColor::LightBlue,
34 anstyle::AnsiColor::BrightMagenta => RatatuiColor::LightMagenta,
35 anstyle::AnsiColor::BrightCyan => RatatuiColor::LightCyan,
36 anstyle::AnsiColor::BrightWhite => RatatuiColor::White,
37 },
38 anstyle::Color::Ansi256(c) => RatatuiColor::Indexed(c.0),
39 anstyle::Color::Rgb(c) => RatatuiColor::Rgb(c.0, c.1, c.2),
40 }
41}
42
43#[derive(Clone, Copy, Debug, PartialEq, Eq)]
47pub enum DiffLineType {
48 Insert,
49 Delete,
50 Context,
51}
52
53pub fn style_line_bg(kind: DiffLineType, theme: DiffTheme, level: DiffColorLevel) -> RatatuiStyle {
55 match kind {
56 DiffLineType::Insert => {
57 RatatuiStyle::default().bg(ratatui_color_from_anstyle(diff_add_bg(theme, level)))
58 }
59 DiffLineType::Delete => {
60 RatatuiStyle::default().bg(ratatui_color_from_anstyle(diff_del_bg(theme, level)))
61 }
62 DiffLineType::Context => RatatuiStyle::default(),
63 }
64}
65
66pub fn style_gutter(kind: DiffLineType, theme: DiffTheme, level: DiffColorLevel) -> RatatuiStyle {
70 let _ = (theme, level);
71 match kind {
72 DiffLineType::Insert => RatatuiStyle::default()
73 .fg(RatatuiColor::Green)
74 .add_modifier(Modifier::DIM)
75 .remove_modifier(Modifier::BOLD),
76 DiffLineType::Delete => RatatuiStyle::default()
77 .fg(RatatuiColor::Red)
78 .add_modifier(Modifier::DIM)
79 .remove_modifier(Modifier::BOLD),
80 DiffLineType::Context => RatatuiStyle::default().add_modifier(Modifier::DIM),
81 }
82}
83
84pub fn style_sign(kind: DiffLineType, _theme: DiffTheme, _level: DiffColorLevel) -> RatatuiStyle {
87 match kind {
88 DiffLineType::Insert => RatatuiStyle::default()
89 .fg(RatatuiColor::Green)
90 .add_modifier(Modifier::DIM)
91 .remove_modifier(Modifier::BOLD),
92 DiffLineType::Delete => RatatuiStyle::default()
93 .fg(RatatuiColor::Red)
94 .add_modifier(Modifier::DIM)
95 .remove_modifier(Modifier::BOLD),
96 DiffLineType::Context => RatatuiStyle::default(),
97 }
98}
99
100pub fn style_content(kind: DiffLineType, theme: DiffTheme, level: DiffColorLevel) -> RatatuiStyle {
106 match (kind, theme, level) {
107 (DiffLineType::Insert, DiffTheme::Dark, DiffColorLevel::Ansi16) => RatatuiStyle::default()
109 .fg(RatatuiColor::Black)
110 .bg(ratatui_color_from_anstyle(diff_add_bg(theme, level))),
111 (DiffLineType::Delete, DiffTheme::Dark, DiffColorLevel::Ansi16) => RatatuiStyle::default()
112 .fg(RatatuiColor::Black)
113 .bg(ratatui_color_from_anstyle(diff_del_bg(theme, level))),
114 (DiffLineType::Insert, DiffTheme::Light, _) => {
116 RatatuiStyle::default().bg(ratatui_color_from_anstyle(diff_add_bg(theme, level)))
117 }
118 (DiffLineType::Delete, DiffTheme::Light, _) => {
119 RatatuiStyle::default().bg(ratatui_color_from_anstyle(diff_del_bg(theme, level)))
120 }
121 (DiffLineType::Insert, DiffTheme::Dark, _) => RatatuiStyle::default()
123 .fg(RatatuiColor::Green)
124 .bg(ratatui_color_from_anstyle(diff_add_bg(theme, level))),
125 (DiffLineType::Delete, DiffTheme::Dark, _) => RatatuiStyle::default()
126 .fg(RatatuiColor::Red)
127 .bg(ratatui_color_from_anstyle(diff_del_bg(theme, level))),
128 (DiffLineType::Context, _, _) => RatatuiStyle::default(),
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136
137 #[test]
138 fn dark_truecolor_add_bg_is_rgb() {
139 let bg = diff_add_bg(DiffTheme::Dark, DiffColorLevel::TrueColor);
140 assert!(matches!(
141 bg,
142 anstyle::Color::Rgb(anstyle::RgbColor(25, 45, 35))
143 ));
144 }
145
146 #[test]
147 fn dark_truecolor_del_bg_is_rgb() {
148 let bg = diff_del_bg(DiffTheme::Dark, DiffColorLevel::TrueColor);
149 assert!(matches!(
150 bg,
151 anstyle::Color::Rgb(anstyle::RgbColor(90, 40, 40))
152 ));
153 }
154
155 #[test]
156 fn light_truecolor_add_bg_is_accessible() {
157 let bg = diff_add_bg(DiffTheme::Light, DiffColorLevel::TrueColor);
158 assert!(matches!(
159 bg,
160 anstyle::Color::Rgb(anstyle::RgbColor(215, 240, 215))
161 ));
162 }
163
164 #[test]
165 fn light_truecolor_del_bg_is_accessible() {
166 let bg = diff_del_bg(DiffTheme::Light, DiffColorLevel::TrueColor);
167 assert!(matches!(
168 bg,
169 anstyle::Color::Rgb(anstyle::RgbColor(255, 235, 235))
170 ));
171 }
172
173 #[test]
174 fn dark_256_uses_indexed_colors() {
175 let add = diff_add_bg(DiffTheme::Dark, DiffColorLevel::Ansi256);
176 let del = diff_del_bg(DiffTheme::Dark, DiffColorLevel::Ansi256);
177 assert!(matches!(
178 add,
179 anstyle::Color::Ansi256(anstyle::Ansi256Color(22))
180 ));
181 assert!(matches!(
182 del,
183 anstyle::Color::Ansi256(anstyle::Ansi256Color(52))
184 ));
185 }
186
187 #[test]
188 fn dark_ansi16_uses_named_colors() {
189 let add = diff_add_bg(DiffTheme::Dark, DiffColorLevel::Ansi16);
190 let del = diff_del_bg(DiffTheme::Dark, DiffColorLevel::Ansi16);
191 assert_eq!(add, anstyle::Color::Ansi(anstyle::AnsiColor::Green));
192 assert_eq!(del, anstyle::Color::Ansi(anstyle::AnsiColor::Red));
193 }
194
195 #[test]
196 fn context_line_bg_is_default() {
197 let style = style_line_bg(
198 DiffLineType::Context,
199 DiffTheme::Dark,
200 DiffColorLevel::TrueColor,
201 );
202 assert_eq!(style, RatatuiStyle::default());
203 }
204
205 #[test]
206 fn dark_gutter_is_dim() {
207 let style = style_gutter(
208 DiffLineType::Context,
209 DiffTheme::Dark,
210 DiffColorLevel::TrueColor,
211 );
212 assert!(style.add_modifier.contains(Modifier::DIM));
213 }
214
215 #[test]
216 fn insert_gutter_is_dim_standard_green_no_bold() {
217 let style = style_gutter(
218 DiffLineType::Insert,
219 DiffTheme::Light,
220 DiffColorLevel::TrueColor,
221 );
222 assert_eq!(style.fg, Some(RatatuiColor::Green));
223 assert!(style.add_modifier.contains(Modifier::DIM));
224 assert!(style.sub_modifier.contains(Modifier::BOLD));
225 }
226
227 #[test]
228 fn dark_ansi16_content_forces_black_fg() {
229 let style = style_content(
230 DiffLineType::Insert,
231 DiffTheme::Dark,
232 DiffColorLevel::Ansi16,
233 );
234 assert_eq!(style.fg, Some(RatatuiColor::Black));
235 }
236
237 #[test]
238 fn sign_style_always_uses_standard_colors() {
239 let add_sign = style_sign(
240 DiffLineType::Insert,
241 DiffTheme::Dark,
242 DiffColorLevel::TrueColor,
243 );
244 let del_sign = style_sign(
245 DiffLineType::Delete,
246 DiffTheme::Light,
247 DiffColorLevel::Ansi16,
248 );
249 assert_eq!(add_sign.fg, Some(RatatuiColor::Green));
250 assert_eq!(del_sign.fg, Some(RatatuiColor::Red));
251 assert!(add_sign.add_modifier.contains(Modifier::DIM));
252 assert!(del_sign.add_modifier.contains(Modifier::DIM));
253 assert!(add_sign.sub_modifier.contains(Modifier::BOLD));
254 assert!(del_sign.sub_modifier.contains(Modifier::BOLD));
255 }
256}