Support for all 24 bits of colors

Mikayla Maki created

Change summary

crates/terminal/src/terminal.rs         | 39 +++++++++++++--
crates/terminal/src/terminal_element.rs | 64 ++++++++++++++++++++++++++
2 files changed, 95 insertions(+), 8 deletions(-)

Detailed changes

crates/terminal/src/terminal.rs 🔗

@@ -4,7 +4,7 @@ use alacritty_terminal::{
     event_loop::{EventLoop, Msg, Notifier},
     grid::Scroll,
     sync::FairMutex,
-    term::{color::Rgb, SizeInfo},
+    term::{color::Rgb as AlacRgb, SizeInfo},
     tty, Term,
 };
 
@@ -13,8 +13,8 @@ use futures::{
     StreamExt,
 };
 use gpui::{
-    actions, elements::*, impl_internal_actions, platform::CursorStyle, ClipboardItem, Entity,
-    MutableAppContext, View, ViewContext,
+    actions, color::Color, elements::*, impl_internal_actions, platform::CursorStyle,
+    ClipboardItem, Entity, MutableAppContext, View, ViewContext,
 };
 use project::{Project, ProjectPath};
 use settings::Settings;
@@ -22,7 +22,7 @@ use smallvec::SmallVec;
 use std::{path::PathBuf, sync::Arc};
 use workspace::{Item, Workspace};
 
-use crate::terminal_element::TerminalEl;
+use crate::terminal_element::{get_color_at_index, TerminalEl};
 
 //ASCII Control characters on a keyboard
 //Consts -> Structs -> Impls -> Functions, Vaguely in order of importance
@@ -203,9 +203,26 @@ impl Terminal {
                 cx,
             ),
             AlacTermEvent::ColorRequest(index, format) => {
-                //TODO test this as well
-                //TODO: change to getting the display colors, like alacrityy, instead of a default
-                let color = self.term.lock().colors()[index].unwrap_or(Rgb::default());
+                let color = self.term.lock().colors()[index].unwrap_or_else(|| {
+                    let term_style = &cx.global::<Settings>().theme.terminal;
+                    match index {
+                        0..=255 => to_alac_rgb(get_color_at_index(&(index as u8), term_style)),
+                        256 => to_alac_rgb(term_style.foreground),
+                        257 => to_alac_rgb(term_style.background),
+                        258 => to_alac_rgb(term_style.cursor),
+                        259 => to_alac_rgb(term_style.dim_black),
+                        260 => to_alac_rgb(term_style.dim_red),
+                        261 => to_alac_rgb(term_style.dim_green),
+                        262 => to_alac_rgb(term_style.dim_yellow),
+                        263 => to_alac_rgb(term_style.dim_blue),
+                        264 => to_alac_rgb(term_style.dim_magenta),
+                        265 => to_alac_rgb(term_style.dim_cyan),
+                        266 => to_alac_rgb(term_style.dim_white),
+                        267 => to_alac_rgb(term_style.bright_foreground),
+                        268 => to_alac_rgb(term_style.black), //Dim Background, non-standard
+                        _ => AlacRgb { r: 0, g: 0, b: 0 },
+                    }
+                });
                 self.write_to_pty(&Input(format(color)), cx)
             }
             AlacTermEvent::CursorBlinkingChange => {
@@ -420,6 +437,14 @@ impl Item for Terminal {
     }
 }
 
+fn to_alac_rgb(color: Color) -> AlacRgb {
+    AlacRgb {
+        r: color.r,
+        g: color.g,
+        b: color.g,
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;

crates/terminal/src/terminal_element.rs 🔗

@@ -329,10 +329,59 @@ fn alac_color_to_gpui_color(allac_color: &AnsiColor, style: &TerminalStyle) -> C
             alacritty_terminal::ansi::NamedColor::DimForeground => style.dim_foreground,
         }, //Theme defined
         alacritty_terminal::ansi::Color::Spec(rgb) => Color::new(rgb.r, rgb.g, rgb.b, 1),
-        alacritty_terminal::ansi::Color::Indexed(_) => Color::white(), //Color cube weirdness
+        alacritty_terminal::ansi::Color::Indexed(i) => get_color_at_index(i, style), //Color cube weirdness
     }
 }
 
+pub fn get_color_at_index(index: &u8, style: &TerminalStyle) -> Color {
+    match index {
+        0 => style.black,
+        1 => style.red,
+        2 => style.green,
+        3 => style.yellow,
+        4 => style.blue,
+        5 => style.magenta,
+        6 => style.cyan,
+        7 => style.white,
+        8 => style.bright_black,
+        9 => style.bright_red,
+        10 => style.bright_green,
+        11 => style.bright_yellow,
+        12 => style.bright_blue,
+        13 => style.bright_magenta,
+        14 => style.bright_cyan,
+        15 => style.bright_white,
+        16..=231 => {
+            let (r, g, b) = rgb_for_index(index); //Split the index into it's rgb components
+            let step = (u8::MAX as f32 / 5.).round() as u8; //Split the GPUI range into 5 chunks
+            Color::new(r * step, g * step, b * step, 1) //Map the rgb components to GPUI's range
+        }
+        //Grayscale from black to white, 0 to 24
+        232..=255 => {
+            let i = 24 - (index - 232); //Align index to 24..0
+            let step = (u8::MAX as f32 / 24.).round() as u8; //Split the 256 range grayscale into 24 chunks
+            Color::new(i * step, i * step, i * step, 1) //Map the rgb components to GPUI's range
+        }
+    }
+}
+
+///Generates the rgb channels in [0, 5] for a given index into the 6x6x6 ANSI color cube
+///See: [8 bit ansi color](https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit).
+///
+///Wikipedia gives a formula for calculating the index for a given color:
+///
+///index = 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5)
+///
+///This function does the reverse, calculating the r, g, and b components from a given index.
+fn rgb_for_index(i: &u8) -> (u8, u8, u8) {
+    debug_assert!(i >= &16 && i <= &231);
+    let i = i - 16;
+    let r = (i - (i % 36)) / 36;
+    let g = ((i % 36) - (i % 6)) / 6;
+    let b = (i % 36) % 6;
+    (r, g, b)
+}
+
 #[cfg(debug_assertions)]
 fn draw_debug_grid(bounds: RectF, layout: &mut LayoutState, cx: &mut PaintContext) {
     let width = layout.cur_size.width();
@@ -361,3 +410,16 @@ fn draw_debug_grid(bounds: RectF, layout: &mut LayoutState, cx: &mut PaintContex
         });
     }
 }
+
+mod tests {
+    use crate::terminal_element::rgb_for_index;
+
+    #[test]
+    fn test_rgb_for_index() {
+        //Test every possible value in the color cube
+        for i in 16..=231 {
+            let (r, g, b) = rgb_for_index(&(i as u8));
+            assert_eq!(i, 16 + 36 * r + 6 * g + b);
+        }
+    }
+}