1use std::fmt::Debug;
4
5#[derive(Clone, PartialEq, Eq)]
6pub struct SuggestionColor {
7 pub fg: Option<VTermColor>,
8 pub bg: Option<VTermColor>,
9}
10
11impl SuggestionColor {
12 pub fn fg(&self) -> Option<VTermColor> {
13 self.fg.clone()
14 }
15
16 pub fn bg(&self) -> Option<VTermColor> {
17 self.bg.clone()
18 }
19}
20
21impl Debug for SuggestionColor {
22 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23 f.debug_struct("SuggestionColor")
24 .field("fg", &self.fg())
25 .field("bg", &self.bg())
26 .finish()
27 }
28}
29
30#[derive(Clone, PartialEq, Eq, Debug)]
31pub enum VTermColor {
32 Rgb { red: u8, green: u8, blue: u8 },
33 Indexed { idx: u8 },
34}
35
36impl VTermColor {
37 const fn from_idx(idx: u8) -> Self {
38 VTermColor::Indexed { idx }
39 }
40
41 const fn from_rgb(red: u8, green: u8, blue: u8) -> Self {
42 VTermColor::Rgb { red, green, blue }
43 }
44}
45
46impl From<nu_ansi_term::Color> for VTermColor {
47 fn from(color: nu_ansi_term::Color) -> Self {
48 use nu_ansi_term::Color;
49 match color {
50 Color::Black => VTermColor::from_idx(0),
51 Color::Red => VTermColor::from_idx(1),
52 Color::Green => VTermColor::from_idx(2),
53 Color::Yellow => VTermColor::from_idx(3),
54 Color::Blue => VTermColor::from_idx(4),
55 Color::Purple => VTermColor::from_idx(5),
56 Color::Magenta => VTermColor::from_idx(5),
57 Color::Cyan => VTermColor::from_idx(6),
58 Color::White => VTermColor::from_idx(7),
59 Color::DarkGray => VTermColor::from_idx(8),
60 Color::LightRed => VTermColor::from_idx(9),
61 Color::LightGreen => VTermColor::from_idx(10),
62 Color::LightYellow => VTermColor::from_idx(11),
63 Color::LightBlue => VTermColor::from_idx(12),
64 Color::LightPurple => VTermColor::from_idx(13),
65 Color::LightMagenta => VTermColor::from_idx(13),
66 Color::LightCyan => VTermColor::from_idx(14),
67 Color::LightGray => VTermColor::from_idx(16),
68 Color::Fixed(i) => VTermColor::from_idx(i),
69 Color::Rgb(r, g, b) => VTermColor::from_rgb(r, g, b),
70 Color::Default => VTermColor::from_idx(7),
71 }
72 }
73}
74
75bitflags::bitflags! {
76 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
77 pub struct ColorSupport: u32 {
78 const TERM256 = 1 << 1;
79 const TERM24BIT = 1 << 2;
80 }
81}
82
83#[allow(clippy::if_same_then_else)]
84pub fn get_color_support() -> ColorSupport {
86 let mut support_term256 = false;
89 let mut support_term24bit = false;
90
91 let term = std::env::var("TERM").ok();
92 let fish_term256 = std::env::var("fish_term256").ok();
93
94 if let Some(fish_term256) = fish_term256 {
95 support_term256 = bool_from_string(&fish_term256);
96 } else if term.is_some() && term.as_ref().unwrap().contains("256color") {
97 support_term256 = true;
98 } else if term.is_some() && term.as_ref().unwrap().contains("xterm") {
99 let term_program = std::env::var("TERM_PROGRAM").ok();
101 if term_program.is_some() && term_program.unwrap() == "Apple_Terminal" {
102 let tpv = std::env::var("TERM_PROGRAM_VERSION").ok();
103 if tpv.is_some() && tpv.unwrap().parse::<i32>().unwrap_or(0) > 299 {
104 support_term256 = true;
105 }
106 } else {
107 support_term256 = true;
108 }
109 }
110
111 let ct = std::env::var("COLORTERM").ok();
112 let it = std::env::var("ITERM_SESSION_ID").ok();
113 let vte = std::env::var("VTE_VERSION").ok();
114 let fish_term24bit = std::env::var("fish_term24bit").ok();
115 if let Some(fish_term24bit) = fish_term24bit {
117 support_term24bit = bool_from_string(&fish_term24bit);
118 } else if std::env::var("STY").is_ok() || (term.is_some() && term.as_ref().unwrap().starts_with("eterm")) {
119 support_term24bit = false;
122 } else if let Some(ct) = ct {
123 if ct == "truecolor" || ct == "24bit" {
125 support_term24bit = true;
126 }
127 } else if std::env::var("KONSOLE_VERSION").is_ok() || std::env::var("KONSOLE_PROFILE_NAME").is_ok() {
128 support_term24bit = true;
131 } else if it.is_some() {
132 if it.unwrap().contains(':') {
136 support_term24bit = true;
137 }
138 } else if term.as_ref().is_some() && term.unwrap().starts_with("st-") {
139 support_term24bit = true;
140 } else if vte.is_some() && vte.unwrap().parse::<i32>().unwrap_or(0) > 3600 {
141 support_term24bit = true;
142 }
143
144 let mut support = ColorSupport::empty();
145
146 if support_term256 {
147 support |= ColorSupport::TERM256;
148 }
149
150 if support_term24bit {
151 support |= ColorSupport::TERM24BIT;
152 }
153
154 support
155}
156
157pub fn parse_suggestion_color_zsh_autosuggest(suggestion_str: &str, color_support: ColorSupport) -> SuggestionColor {
158 let mut sc = SuggestionColor { fg: None, bg: None };
159
160 for mut color_name in suggestion_str.split(',') {
161 let is_fg = color_name.starts_with("fg=");
162 let is_bg = color_name.starts_with("bg=");
163 if is_fg || is_bg {
164 (_, color_name) = color_name.split_at(3);
165 let mut color = try_parse_named(color_name);
171 if color.is_none() && color_name.starts_with('#') {
172 color = try_parse_rgb(color_name);
173 }
174 if color.is_none() {
175 let index = color_name.parse::<u64>().unwrap_or(0);
177 let index_supported = if color_support.is_empty() {
178 index < 16
179 } else {
180 index < 256
181 };
182 if index_supported {
183 let vc = VTermColor::Indexed { idx: index as u8 };
184 if is_fg {
185 sc.fg = Some(vc);
186 } else {
187 sc.bg = Some(vc);
188 }
189 }
190 } else {
191 let vc = color_to_vterm_color(color, color_support);
192 if is_fg {
193 sc.fg = vc;
194 } else {
195 sc.bg = vc;
196 }
197 }
198 }
199 }
200
201 sc
202}
203
204pub fn parse_suggestion_color_fish(suggestion_str: &str, color_support: ColorSupport) -> Option<SuggestionColor> {
205 let c = parse_fish_color_from_string(suggestion_str, color_support);
206 let vc = color_to_vterm_color(c, color_support)?;
207 Some(SuggestionColor { fg: Some(vc), bg: None })
208}
209
210pub fn parse_hint_color_nu(suggestion_str: impl AsRef<str>) -> SuggestionColor {
211 let color = nu_color_config::lookup_ansi_color_style(suggestion_str.as_ref());
212 SuggestionColor {
213 fg: color.foreground.map(VTermColor::from),
214 bg: color.background.map(VTermColor::from),
215 }
216}
217
218#[derive(PartialEq, Eq, Debug)]
219#[repr(u8)]
220enum ColorType {
221 Named = 1,
222 Rgb = 2,
223}
224
225#[derive(Debug, PartialEq, Eq)]
226struct Color {
227 kind: ColorType,
228 name_idx: u8,
229 rgb: [u8; 3],
230}
231
232fn bool_from_string(x: &str) -> bool {
233 match x.chars().next() {
234 Some(first) => "YTyt1".contains(first),
235 None => false,
236 }
237}
238
239const fn squared_difference(p1: i64, p2: i64) -> u64 {
240 let diff = (p1 - p2).unsigned_abs();
241 diff * diff
242}
243
244const fn convert_color(rgb: [u8; 3], colors: &[u32]) -> u8 {
245 let r = rgb[0] as i64;
246 let g = rgb[1] as i64;
247 let b = rgb[2] as i64;
248
249 let mut best_distance = u64::MAX;
250 let mut best_index = u8::MAX;
251
252 let mut i = 0;
253 while i < colors.len() {
254 let color = colors[i];
255 let test_r = ((color >> 16) & 0xff) as i64;
256 let test_g = ((color >> 8) & 0xff) as i64;
257 let test_b = (color & 0xff) as i64;
258 let distance = squared_difference(r, test_r) + squared_difference(g, test_g) + squared_difference(b, test_b);
259 if distance <= best_distance {
260 best_index = i as u8;
261 best_distance = distance;
262 }
263 i += 1;
264 }
265
266 best_index
267}
268
269fn try_parse_rgb(name: &str) -> Option<Color> {
272 let name = match name.strip_prefix('#') {
274 Some(name) => name,
275 None => name,
276 };
277
278 let mut color = Color {
279 kind: ColorType::Rgb,
280 name_idx: 0,
281 rgb: [0, 0, 0],
282 };
283
284 let name = name.as_bytes();
285
286 match name.len() {
287 3 => {
289 for (i, c) in name.iter().enumerate().take(3) {
290 let val = char::from(*c).to_digit(16)? as u8;
291 color.rgb[i] = val * 16 + val;
292 }
293 Some(color)
294 },
295 6 => {
297 for i in 0..3 {
298 let val_hi = char::from(name[i * 2]).to_digit(16)? as u8;
299 let val_low = char::from(name[i * 2 + 1]).to_digit(16)? as u8;
300 color.rgb[i] = val_hi * 16 + val_low;
301 }
302 Some(color)
303 },
304 _ => None,
305 }
306}
307
308struct NamedColor {
309 name: &'static str,
310 idx: u8,
311 _rgb: [u8; 3],
312}
313
314macro_rules! decl_named_colors {
315 ($({$name: expr, $idx: expr, { $r: expr, $g: expr, $b: expr }}),*,) => {
316 &[
317 $(
318 NamedColor {
319 name: $name,
320 idx: $idx,
321 _rgb: [$r, $g, $b],
322 },
323 )*
324 ]
325 };
326}
327
328static NAMED_COLORS: &[NamedColor] = decl_named_colors! {
330 {"black", 0, {0x00, 0x00, 0x00}}, {"blue", 4, {0x00, 0x00, 0x80}},
331 {"brblack", 8, {0x80, 0x80, 0x80}}, {"brblue", 12, {0x00, 0x00, 0xFF}},
332 {"brbrown", 11, {0xFF, 0xFF, 0x00}}, {"brcyan", 14, {0x00, 0xFF, 0xFF}},
333 {"brgreen", 10, {0x00, 0xFF, 0x00}}, {"brgrey", 8, {0x55, 0x55, 0x55}},
334 {"brmagenta", 13, {0xFF, 0x00, 0xFF}}, {"brown", 3, {0x72, 0x50, 0x00}},
335 {"brpurple", 13, {0xFF, 0x00, 0xFF}}, {"brred", 9, {0xFF, 0x00, 0x00}},
336 {"brwhite", 15, {0xFF, 0xFF, 0xFF}}, {"bryellow", 11, {0xFF, 0xFF, 0x00}},
337 {"cyan", 6, {0x00, 0x80, 0x80}}, {"green", 2, {0x00, 0x80, 0x00}},
338 {"grey", 7, {0xE5, 0xE5, 0xE5}}, {"magenta", 5, {0x80, 0x00, 0x80}},
339 {"purple", 5, {0x80, 0x00, 0x80}}, {"red", 1, {0x80, 0x00, 0x00}},
340 {"white", 7, {0xC0, 0xC0, 0xC0}}, {"yellow", 3, {0x80, 0x80, 0x00}},
341};
342
343fn try_parse_named(s: &str) -> Option<Color> {
344 let idx_res = NAMED_COLORS.binary_search_by(|elem| elem.name.cmp(&s.to_ascii_lowercase()));
345 if let Ok(idx) = idx_res {
346 return Some(Color {
347 kind: ColorType::Named,
348 name_idx: NAMED_COLORS[idx].idx,
349 rgb: [0, 0, 0],
350 });
351 }
352 None
353}
354
355const fn term16_color_for_rgb(rgb: [u8; 3]) -> u8 {
356 const K_COLORS: &[u32] = &[
357 0x000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080, 0x008080, 0xc0c0c0, 0x808080, 0xff0000, 0x00ff00, 0xffff00, 0x0000ff, 0xff00ff, 0x00ffff, 0xffffff, ];
374 convert_color(rgb, K_COLORS)
375}
376
377const fn term256_color_for_rgb(rgb: [u8; 3]) -> u8 {
378 const K_COLORS: &[u32] = &[
379 0x000000, 0x00005f, 0x000087, 0x0000af, 0x0000d7, 0x0000ff, 0x005f00, 0x005f5f, 0x005f87, 0x005faf, 0x005fd7,
380 0x005fff, 0x008700, 0x00875f, 0x008787, 0x0087af, 0x0087d7, 0x0087ff, 0x00af00, 0x00af5f, 0x00af87, 0x00afaf,
381 0x00afd7, 0x00afff, 0x00d700, 0x00d75f, 0x00d787, 0x00d7af, 0x00d7d7, 0x00d7ff, 0x00ff00, 0x00ff5f, 0x00ff87,
382 0x00ffaf, 0x00ffd7, 0x00ffff, 0x5f0000, 0x5f005f, 0x5f0087, 0x5f00af, 0x5f00d7, 0x5f00ff, 0x5f5f00, 0x5f5f5f,
383 0x5f5f87, 0x5f5faf, 0x5f5fd7, 0x5f5fff, 0x5f8700, 0x5f875f, 0x5f8787, 0x5f87af, 0x5f87d7, 0x5f87ff, 0x5faf00,
384 0x5faf5f, 0x5faf87, 0x5fafaf, 0x5fafd7, 0x5fafff, 0x5fd700, 0x5fd75f, 0x5fd787, 0x5fd7af, 0x5fd7d7, 0x5fd7ff,
385 0x5fff00, 0x5fff5f, 0x5fff87, 0x5fffaf, 0x5fffd7, 0x5fffff, 0x870000, 0x87005f, 0x870087, 0x8700af, 0x8700d7,
386 0x8700ff, 0x875f00, 0x875f5f, 0x875f87, 0x875faf, 0x875fd7, 0x875fff, 0x878700, 0x87875f, 0x878787, 0x8787af,
387 0x8787d7, 0x8787ff, 0x87af00, 0x87af5f, 0x87af87, 0x87afaf, 0x87afd7, 0x87afff, 0x87d700, 0x87d75f, 0x87d787,
388 0x87d7af, 0x87d7d7, 0x87d7ff, 0x87ff00, 0x87ff5f, 0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff, 0xaf0000, 0xaf005f,
389 0xaf0087, 0xaf00af, 0xaf00d7, 0xaf00ff, 0xaf5f00, 0xaf5f5f, 0xaf5f87, 0xaf5faf, 0xaf5fd7, 0xaf5fff, 0xaf8700,
390 0xaf875f, 0xaf8787, 0xaf87af, 0xaf87d7, 0xaf87ff, 0xafaf00, 0xafaf5f, 0xafaf87, 0xafafaf, 0xafafd7, 0xafafff,
391 0xafd700, 0xafd75f, 0xafd787, 0xafd7af, 0xafd7d7, 0xafd7ff, 0xafff00, 0xafff5f, 0xafff87, 0xafffaf, 0xafffd7,
392 0xafffff, 0xd70000, 0xd7005f, 0xd70087, 0xd700af, 0xd700d7, 0xd700ff, 0xd75f00, 0xd75f5f, 0xd75f87, 0xd75faf,
393 0xd75fd7, 0xd75fff, 0xd78700, 0xd7875f, 0xd78787, 0xd787af, 0xd787d7, 0xd787ff, 0xd7af00, 0xd7af5f, 0xd7af87,
394 0xd7afaf, 0xd7afd7, 0xd7afff, 0xd7d700, 0xd7d75f, 0xd7d787, 0xd7d7af, 0xd7d7d7, 0xd7d7ff, 0xd7ff00, 0xd7ff5f,
395 0xd7ff87, 0xd7ffaf, 0xd7ffd7, 0xd7ffff, 0xff0000, 0xff005f, 0xff0087, 0xff00af, 0xff00d7, 0xff00ff, 0xff5f00,
396 0xff5f5f, 0xff5f87, 0xff5faf, 0xff5fd7, 0xff5fff, 0xff8700, 0xff875f, 0xff8787, 0xff87af, 0xff87d7, 0xff87ff,
397 0xffaf00, 0xffaf5f, 0xffaf87, 0xffafaf, 0xffafd7, 0xffafff, 0xffd700, 0xffd75f, 0xffd787, 0xffd7af, 0xffd7d7,
398 0xffd7ff, 0xffff00, 0xffff5f, 0xffff87, 0xffffaf, 0xffffd7, 0xffffff, 0x080808, 0x121212, 0x1c1c1c, 0x262626,
399 0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e, 0x585858, 0x626262, 0x6c6c6c, 0x767676, 0x808080, 0x8a8a8a, 0x949494,
400 0x9e9e9e, 0xa8a8a8, 0xb2b2b2, 0xbcbcbc, 0xc6c6c6, 0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee,
401 ];
402 16 + convert_color(rgb, K_COLORS)
403}
404
405fn parse_fish_color_from_string(s: &str, color_support: ColorSupport) -> Option<Color> {
406 let mut first_rgb = None;
407 let mut first_named = None;
408
409 for color_name in s.split([' ', '\t']) {
410 if !color_name.starts_with('-') {
411 let mut color = try_parse_named(color_name);
412 if color.is_none() {
413 color = try_parse_rgb(color_name);
414 }
415 if let Some(color) = color {
416 if first_rgb.is_none() && color.kind == ColorType::Rgb {
417 first_rgb = Some(color);
418 } else if first_named.is_none() && color.kind == ColorType::Named {
419 first_named = Some(color);
420 }
421 }
422 }
423 }
424
425 if (first_rgb.is_some() && color_support.contains(ColorSupport::TERM24BIT)) || first_named.is_none() {
426 return first_rgb;
427 }
428
429 first_named
430}
431
432fn color_to_vterm_color(c: Option<Color>, color_support: ColorSupport) -> Option<VTermColor> {
433 let c = c?;
434 if c.kind == ColorType::Rgb {
435 if color_support.contains(ColorSupport::TERM24BIT) {
436 Some(VTermColor::from_rgb(c.rgb[0], c.rgb[1], c.rgb[2]))
437 } else if color_support.contains(ColorSupport::TERM256) {
438 Some(VTermColor::from_idx(term256_color_for_rgb(c.rgb)))
439 } else {
440 Some(VTermColor::from_idx(term16_color_for_rgb(c.rgb)))
441 }
442 } else {
443 Some(VTermColor::from_idx(c.name_idx))
444 }
445}
446
447#[cfg(test)]
448mod test {
449 use super::*;
450
451 #[test]
452 fn color_support() {
453 get_color_support();
455
456 for (key, _) in std::env::vars() {
457 std::env::remove_var(key);
458 }
459
460 let assert_supports = |vars: &[(&str, &str)], expected: ColorSupport| {
461 for (key, value) in vars {
462 std::env::set_var(key, value);
463 }
464
465 assert_eq!(get_color_support(), expected);
466
467 for (key, _) in vars {
468 std::env::remove_var(key);
469 }
470 };
471
472 assert_supports(&[], ColorSupport::empty());
474
475 assert_supports(&[("fish_term256", "y")], ColorSupport::TERM256);
478 assert_supports(&[("fish_term256", "n")], ColorSupport::empty());
479 assert_supports(&[("TERM", "foo_256color_bar")], ColorSupport::TERM256);
481 assert_supports(&[("TERM", "xterm")], ColorSupport::TERM256);
483 assert_supports(
485 &[
486 ("TERM", "xterm"),
487 ("TERM_PROGRAM", "Apple_Terminal"),
488 ("TERM_PROGRAM_VERSION", "300"),
489 ],
490 ColorSupport::TERM256,
491 );
492 assert_supports(
494 &[
495 ("TERM", "xterm"),
496 ("TERM_PROGRAM", "Apple_Terminal"),
497 ("TERM_PROGRAM_VERSION", "200"),
498 ],
499 ColorSupport::empty(),
500 );
501
502 assert_supports(&[("fish_term24bit", "y")], ColorSupport::TERM24BIT);
505 assert_supports(&[("fish_term24bit", "n")], ColorSupport::empty());
506 assert_supports(&[("TERM", "eterm"), ("STY", "foo")], ColorSupport::empty());
508 assert_supports(&[("COLORTERM", "truecolor")], ColorSupport::TERM24BIT);
510 assert_supports(&[("COLORTERM", "24bit")], ColorSupport::TERM24BIT);
511 assert_supports(&[("COLORTERM", "foo")], ColorSupport::empty());
512 assert_supports(&[("KONSOLE_VERSION", "foo")], ColorSupport::TERM24BIT);
514 assert_supports(&[("ITERM_SESSION_ID", "1:2")], ColorSupport::TERM24BIT);
516 assert_supports(&[("TERM", "st-foo")], ColorSupport::TERM24BIT);
518 assert_supports(&[("VTE_VERSION", "3500")], ColorSupport::empty());
520 assert_supports(&[("VTE_VERSION", "3700")], ColorSupport::TERM24BIT);
521 }
522
523 #[test]
524 fn assert_named_colors_sort() {
525 NAMED_COLORS
526 .windows(2)
527 .for_each(|elems| assert!(elems[0].name.cmp(elems[1].name).is_lt()));
528 }
529
530 #[test]
531 fn parse_color() {
532 assert!(try_parse_rgb("#ffffff").is_some());
535 assert!(try_parse_rgb("#000000").is_some());
536 assert!(try_parse_rgb("#ababab").is_some());
537 assert!(try_parse_rgb("000000").is_some());
538 assert!(try_parse_rgb("ffffff").is_some());
539 assert!(try_parse_rgb("abcabc").is_some());
540 assert!(try_parse_rgb("#123").is_some());
541 assert!(try_parse_rgb("#fff").is_some());
542 assert!(try_parse_rgb("abc").is_some());
543 assert!(try_parse_rgb("123").is_some());
544 assert!(try_parse_rgb("fff").is_some());
545 assert!(try_parse_rgb("000").is_some());
546
547 assert!(try_parse_rgb("#xyz").is_none());
549 assert!(try_parse_rgb("12").is_none());
550 assert!(try_parse_rgb("abcdeh").is_none());
551 assert!(try_parse_rgb("#ffff").is_none());
552 assert!(try_parse_rgb("12345").is_none());
553 assert!(try_parse_rgb("1234567").is_none());
554
555 assert!(try_parse_named("blue").is_some());
558 assert!(try_parse_named("white").is_some());
559 assert!(try_parse_named("yellow").is_some());
560 assert!(try_parse_named("brblack").is_some());
561 assert!(try_parse_named("BrBlue").is_some());
562 assert!(try_parse_named("bRYelLow").is_some());
563
564 assert!(try_parse_named("aaa").is_none());
566 assert!(try_parse_named("blu").is_none());
567 assert!(try_parse_named("other").is_none());
568 }
569
570 #[test]
571 fn parse_fish_autosuggest() {
572 assert_eq!(
573 parse_fish_color_from_string("cyan", ColorSupport::TERM256),
574 Some(Color {
575 kind: ColorType::Named,
576 name_idx: 6,
577 rgb: [0, 0, 0]
578 })
579 );
580 assert_eq!(
581 parse_fish_color_from_string("#123", ColorSupport::TERM256),
582 Some(Color {
583 kind: ColorType::Rgb,
584 name_idx: 0,
585 rgb: [0x11, 0x22, 0x33]
586 })
587 );
588 assert_eq!(
589 parse_fish_color_from_string("-ignore\t-white\t-#123\tcyan", ColorSupport::TERM256),
590 Some(Color {
591 kind: ColorType::Named,
592 name_idx: 6,
593 rgb: [0, 0, 0]
594 })
595 );
596 assert_eq!(
597 parse_fish_color_from_string("555 brblack", ColorSupport::TERM256),
598 Some(Color {
599 kind: ColorType::Named,
600 name_idx: 8,
601 rgb: [0, 0, 0]
602 })
603 );
604 assert_eq!(
605 parse_fish_color_from_string("555 brblack", ColorSupport::TERM24BIT),
606 Some(Color {
607 kind: ColorType::Rgb,
608 name_idx: 0,
609 rgb: [0x55, 0x55, 0x55]
610 })
611 );
612 assert_eq!(
613 parse_fish_color_from_string("-ignore -all", ColorSupport::TERM256),
614 None
615 );
616 }
617
618 #[test]
619 fn parse_zsh_autosuggest() {
620 assert_eq!(
621 parse_suggestion_color_zsh_autosuggest("fg=#123,bg=#456", ColorSupport::TERM24BIT),
623 SuggestionColor {
624 fg: Some(VTermColor::from_rgb(0x11, 0x22, 0x33)),
625 bg: Some(VTermColor::from_rgb(0x44, 0x55, 0x66)),
626 }
627 );
628 assert_eq!(
629 parse_suggestion_color_zsh_autosuggest("fg=#123,bg=#456", ColorSupport::empty()),
631 SuggestionColor {
632 fg: Some(VTermColor::from_idx(0)),
633 bg: Some(VTermColor::from_idx(8)),
634 }
635 );
636 assert_eq!(
637 parse_suggestion_color_zsh_autosuggest("fg=8", ColorSupport::empty()),
639 SuggestionColor {
640 fg: Some(VTermColor::from_idx(8)),
641 bg: None,
642 }
643 );
644 assert_eq!(
645 parse_suggestion_color_zsh_autosuggest("invalid=!,,=,bg=cyan", ColorSupport::empty()),
647 SuggestionColor {
648 fg: None,
649 bg: Some(VTermColor::from_idx(6))
650 }
651 );
652 }
653}