1use owo_colors::Rgb;
4use palette::{FromColor, Lch, LinSrgb, Mix, Srgb};
5
6#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct DiffTheme {
12 pub deleted: Rgb,
14
15 pub inserted: Rgb,
17
18 pub moved: Rgb,
20
21 pub unchanged: Rgb,
23
24 pub key: Rgb,
26
27 pub structure: Rgb,
29
30 pub comment: Rgb,
32
33 pub string: Rgb,
36
37 pub number: Rgb,
39
40 pub boolean: Rgb,
42
43 pub null: Rgb,
45
46 pub deleted_line_bg: Option<Rgb>,
48
49 pub deleted_highlight_bg: Option<Rgb>,
51
52 pub inserted_line_bg: Option<Rgb>,
54
55 pub inserted_highlight_bg: Option<Rgb>,
57
58 pub moved_line_bg: Option<Rgb>,
60
61 pub moved_highlight_bg: Option<Rgb>,
63}
64
65impl Default for DiffTheme {
66 fn default() -> Self {
67 Self::COLORBLIND_WITH_BG
68 }
69}
70
71impl DiffTheme {
72 pub const COLORBLIND_ORANGE_BLUE: Self = Self {
74 deleted: Rgb(255, 167, 89), inserted: Rgb(97, 175, 239), moved: Rgb(198, 120, 221), unchanged: Rgb(140, 140, 140), key: Rgb(140, 140, 140), structure: Rgb(220, 220, 220), comment: Rgb(100, 100, 100), string: Rgb(152, 195, 121), number: Rgb(209, 154, 102), boolean: Rgb(209, 154, 102), null: Rgb(86, 182, 194), deleted_line_bg: None,
86 deleted_highlight_bg: None,
87 inserted_line_bg: None,
88 inserted_highlight_bg: None,
89 moved_line_bg: None,
90 moved_highlight_bg: None,
91 };
92
93 pub const COLORBLIND_WITH_BG: Self = Self {
95 deleted: Rgb(229, 192, 123), inserted: Rgb(97, 175, 239), moved: Rgb(198, 120, 221), unchanged: Rgb(140, 140, 140), key: Rgb(140, 140, 140), structure: Rgb(220, 220, 220), comment: Rgb(100, 100, 100), string: Rgb(152, 195, 121), number: Rgb(209, 154, 102), boolean: Rgb(209, 154, 102), null: Rgb(86, 182, 194), deleted_line_bg: Some(Rgb(55, 48, 35)), inserted_line_bg: Some(Rgb(35, 48, 60)), moved_line_bg: Some(Rgb(50, 40, 60)), deleted_highlight_bg: Some(Rgb(90, 75, 50)), inserted_highlight_bg: Some(Rgb(45, 70, 95)), moved_highlight_bg: Some(Rgb(80, 55, 95)), };
115
116 pub const PASTEL: Self = Self {
118 deleted: Rgb(255, 138, 128), inserted: Rgb(128, 203, 156), moved: Rgb(128, 179, 255), unchanged: Rgb(140, 140, 140), key: Rgb(140, 140, 140), structure: Rgb(220, 220, 220), comment: Rgb(100, 100, 100), string: Rgb(152, 195, 121), number: Rgb(209, 154, 102), boolean: Rgb(209, 154, 102), null: Rgb(86, 182, 194), deleted_line_bg: None,
130 deleted_highlight_bg: None,
131 inserted_line_bg: None,
132 inserted_highlight_bg: None,
133 moved_line_bg: None,
134 moved_highlight_bg: None,
135 };
136
137 pub const ONE_DARK_PRO: Self = Self {
139 deleted: Rgb(224, 108, 117), inserted: Rgb(152, 195, 121), moved: Rgb(97, 175, 239), unchanged: Rgb(171, 178, 191), key: Rgb(171, 178, 191), structure: Rgb(171, 178, 191), comment: Rgb(92, 99, 112), string: Rgb(152, 195, 121), number: Rgb(209, 154, 102), boolean: Rgb(209, 154, 102), null: Rgb(86, 182, 194), deleted_line_bg: None,
151 deleted_highlight_bg: None,
152 inserted_line_bg: None,
153 inserted_highlight_bg: None,
154 moved_line_bg: None,
155 moved_highlight_bg: None,
156 };
157
158 pub const TOKYO_NIGHT: Self = Self {
160 deleted: Rgb(247, 118, 142), inserted: Rgb(158, 206, 106), moved: Rgb(122, 162, 247), unchanged: Rgb(192, 202, 245), key: Rgb(192, 202, 245), structure: Rgb(192, 202, 245), comment: Rgb(86, 95, 137), string: Rgb(158, 206, 106), number: Rgb(255, 158, 100), boolean: Rgb(255, 158, 100), null: Rgb(125, 207, 255), deleted_line_bg: None,
172 deleted_highlight_bg: None,
173 inserted_line_bg: None,
174 inserted_highlight_bg: None,
175 moved_line_bg: None,
176 moved_highlight_bg: None,
177 };
178
179 pub fn color_for(&self, kind: crate::ChangeKind) -> Rgb {
181 match kind {
182 crate::ChangeKind::Unchanged => self.unchanged,
183 crate::ChangeKind::Deleted => self.deleted,
184 crate::ChangeKind::Inserted => self.inserted,
185 crate::ChangeKind::MovedFrom | crate::ChangeKind::MovedTo => self.moved,
186 crate::ChangeKind::Modified => self.deleted, }
188 }
189
190 pub fn blend(a: Rgb, b: Rgb, t: f32) -> Rgb {
193 let a_lin: LinSrgb =
195 Srgb::new(a.0 as f32 / 255.0, a.1 as f32 / 255.0, a.2 as f32 / 255.0).into_linear();
196 let b_lin: LinSrgb =
197 Srgb::new(b.0 as f32 / 255.0, b.1 as f32 / 255.0, b.2 as f32 / 255.0).into_linear();
198
199 let mixed = a_lin.mix(b_lin, t);
201
202 let result: Srgb = mixed.into();
204 Rgb(
205 (result.red * 255.0).round() as u8,
206 (result.green * 255.0).round() as u8,
207 (result.blue * 255.0).round() as u8,
208 )
209 }
210
211 pub fn brighten_saturate(rgb: Rgb, lightness_boost: f32, chroma_boost: f32) -> Rgb {
214 let srgb = Srgb::new(
215 rgb.0 as f32 / 255.0,
216 rgb.1 as f32 / 255.0,
217 rgb.2 as f32 / 255.0,
218 );
219 let mut lch = Lch::from_color(srgb);
220
221 lch.l = (lch.l + lightness_boost * 100.0).min(100.0);
223
224 lch.chroma = (lch.chroma + chroma_boost).min(150.0);
226
227 let result: Srgb = Srgb::from_color(lch);
228 Rgb(
229 (result.red * 255.0).round() as u8,
230 (result.green * 255.0).round() as u8,
231 (result.blue * 255.0).round() as u8,
232 )
233 }
234
235 pub fn desaturate(rgb: Rgb, amount: f32) -> Rgb {
238 let srgb = Srgb::new(
239 rgb.0 as f32 / 255.0,
240 rgb.1 as f32 / 255.0,
241 rgb.2 as f32 / 255.0,
242 );
243 let mut lch = Lch::from_color(srgb);
244
245 lch.chroma *= 1.0 - amount;
247
248 let result: Srgb = Srgb::from_color(lch);
249 Rgb(
250 (result.red * 255.0).round() as u8,
251 (result.green * 255.0).round() as u8,
252 (result.blue * 255.0).round() as u8,
253 )
254 }
255
256 pub fn deleted_key(&self) -> Rgb {
258 Self::blend(self.key, self.deleted, 0.5)
259 }
260
261 pub fn inserted_key(&self) -> Rgb {
263 Self::blend(self.key, self.inserted, 0.5)
264 }
265
266 pub fn deleted_structure(&self) -> Rgb {
268 Self::blend(self.structure, self.deleted, 0.4)
269 }
270
271 pub fn inserted_structure(&self) -> Rgb {
273 Self::blend(self.structure, self.inserted, 0.4)
274 }
275
276 pub fn deleted_comment(&self) -> Rgb {
278 Self::blend(self.comment, self.deleted, 0.35)
279 }
280
281 pub fn inserted_comment(&self) -> Rgb {
283 Self::blend(self.comment, self.inserted, 0.35)
284 }
285
286 pub fn deleted_string(&self) -> Rgb {
290 Self::blend(self.string, self.deleted, 0.7)
291 }
292
293 pub fn inserted_string(&self) -> Rgb {
295 Self::blend(self.string, self.inserted, 0.7)
296 }
297
298 pub fn deleted_number(&self) -> Rgb {
300 Self::blend(self.number, self.deleted, 0.7)
301 }
302
303 pub fn inserted_number(&self) -> Rgb {
305 Self::blend(self.number, self.inserted, 0.7)
306 }
307
308 pub fn deleted_boolean(&self) -> Rgb {
310 Self::blend(self.boolean, self.deleted, 0.7)
311 }
312
313 pub fn inserted_boolean(&self) -> Rgb {
315 Self::blend(self.boolean, self.inserted, 0.7)
316 }
317
318 pub fn deleted_null(&self) -> Rgb {
320 Self::blend(self.null, self.deleted, 0.7)
321 }
322
323 pub fn inserted_null(&self) -> Rgb {
325 Self::blend(self.null, self.inserted, 0.7)
326 }
327
328 pub fn deleted_highlight_string(&self) -> Rgb {
332 Self::brighten_saturate(self.deleted, 0.15, 0.2)
333 }
334
335 pub fn inserted_highlight_string(&self) -> Rgb {
337 Self::brighten_saturate(self.inserted, 0.15, 0.2)
338 }
339
340 pub fn deleted_highlight_number(&self) -> Rgb {
342 Self::brighten_saturate(self.deleted, 0.15, 0.2)
343 }
344
345 pub fn inserted_highlight_number(&self) -> Rgb {
347 Self::brighten_saturate(self.inserted, 0.15, 0.2)
348 }
349
350 pub fn deleted_highlight_boolean(&self) -> Rgb {
352 Self::brighten_saturate(self.deleted, 0.15, 0.2)
353 }
354
355 pub fn inserted_highlight_boolean(&self) -> Rgb {
357 Self::brighten_saturate(self.inserted, 0.15, 0.2)
358 }
359
360 pub fn deleted_highlight_null(&self) -> Rgb {
362 Self::brighten_saturate(self.deleted, 0.15, 0.2)
363 }
364
365 pub fn inserted_highlight_null(&self) -> Rgb {
367 Self::brighten_saturate(self.inserted, 0.15, 0.2)
368 }
369
370 pub fn deleted_highlight_key(&self) -> Rgb {
374 Self::brighten_saturate(self.deleted, 0.15, 0.2)
375 }
376
377 pub fn inserted_highlight_key(&self) -> Rgb {
379 Self::brighten_saturate(self.inserted, 0.15, 0.2)
380 }
381
382 pub fn deleted_highlight_structure(&self) -> Rgb {
384 Self::brighten_saturate(self.deleted, 0.15, 0.2)
385 }
386
387 pub fn inserted_highlight_structure(&self) -> Rgb {
389 Self::brighten_saturate(self.inserted, 0.15, 0.2)
390 }
391
392 pub fn deleted_highlight_comment(&self) -> Rgb {
394 Self::brighten_saturate(self.deleted, 0.15, 0.2)
395 }
396
397 pub fn inserted_highlight_comment(&self) -> Rgb {
399 Self::brighten_saturate(self.inserted, 0.15, 0.2)
400 }
401
402 pub fn desaturated_deleted_line_bg(&self) -> Option<Rgb> {
406 self.deleted_line_bg.map(|bg| Self::desaturate(bg, 0.2))
407 }
408
409 pub fn desaturated_inserted_line_bg(&self) -> Option<Rgb> {
411 self.inserted_line_bg.map(|bg| Self::desaturate(bg, 0.2))
412 }
413
414 pub fn desaturated_moved_line_bg(&self) -> Option<Rgb> {
416 self.moved_line_bg.map(|bg| Self::desaturate(bg, 0.2))
417 }
418
419 pub fn desaturated_deleted_highlight_bg(&self) -> Option<Rgb> {
421 self.deleted_highlight_bg.map(|bg| {
422 let brightened = Self::brighten_saturate(bg, 0.02, -5.0);
423 Self::desaturate(brightened, 0.75)
424 })
425 }
426
427 pub fn desaturated_inserted_highlight_bg(&self) -> Option<Rgb> {
429 self.inserted_highlight_bg.map(|bg| {
430 let brightened = Self::brighten_saturate(bg, 0.02, -5.0);
431 Self::desaturate(brightened, 0.75)
432 })
433 }
434
435 pub fn desaturated_moved_highlight_bg(&self) -> Option<Rgb> {
437 self.moved_highlight_bg.map(|bg| {
438 let brightened = Self::brighten_saturate(bg, 0.02, -5.0);
439 Self::desaturate(brightened, 0.75)
440 })
441 }
442}