diff options
| -rw-r--r-- | src/color_new.rs | 220 | 
1 files changed, 220 insertions, 0 deletions
| diff --git a/src/color_new.rs b/src/color_new.rs new file mode 100644 index 0000000..fac0e2b --- /dev/null +++ b/src/color_new.rs @@ -0,0 +1,220 @@ +#[non_exhaustive] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum Color { +    Black, +    DarkGrey, +    Red, +    DarkRed, +    Green, +    DarkGreen, +    Yellow, +    DarkYellow, +    Blue, +    DarkBlue, +    Magenta, +    DarkMagenta, +    Cyan, +    DarkCyan, +    White, +    Grey, +} + +pub trait YaxColors { +    fn arithmetic_op(&self) -> Color; +    fn stack_op(&self) -> Color; +    fn nop_op(&self) -> Color; +    fn stop_op(&self) -> Color; +    fn control_flow_op(&self) -> Color; +    fn data_op(&self) -> Color; +    fn comparison_op(&self) -> Color; +    fn invalid_op(&self) -> Color; +    fn platform_op(&self) -> Color; +    fn misc_op(&self) -> Color; + +    fn register(&self) -> Color; +    fn program_counter(&self) -> Color; +    fn number(&self) -> Color; +    fn zero(&self) -> Color; +    fn one(&self) -> Color; +    fn minus_one(&self) -> Color; +    fn address(&self) -> Color; +    fn symbol(&self) -> Color; +    fn function(&self) -> Color; +} + +mod ansi { +    use crate::color_new::Color; + +    // color sequences as described by ECMA-48 and, apparently, `man 4 console_codes` +    /// translate [`yaxpeax_arch::color_new::Color`] to an ANSI control code that changes the +    /// foreground color to match. +    #[allow(dead_code)] // allowing this to be dead code because if colors are enabled and alloc is not, there will not be an AnsiDisplaySink, which is the sole user of this function. +    fn color2ansi(color: Color) -> &'static str { +        // for most of these, in 256 color space the darker color can be picked by the same color +        // index as the brighter form (from the 8 color command set). dark grey is an outlier, +        // where 38;5;0 and 30 both are black. there is no "grey" in the shorter command set to +        // map to. but it turns out that 38;5;m is exactly the darker grey to use. +        match color { +            Color::Black => "\x1b[30m", +            Color::DarkGrey => "\x1b[38;5;8m", +            Color::Red => "\x1b[31m", +            Color::DarkRed => "\x1b[38;5;1m", +            Color::Green => "\x1b[32m", +            Color::DarkGreen => "\x1b[38;5;2m", +            Color::Yellow => "\x1b[33m", +            Color::DarkYellow => "\x1b[38;5;3m", +            Color::Blue => "\x1b[34m", +            Color::DarkBlue => "\x1b[38;5;4m", +            Color::Magenta => "\x1b[35m", +            Color::DarkMagenta => "\x1b[38;5;5m", +            Color::Cyan => "\x1b[36m", +            Color::DarkCyan => "\x1b[38;5;6m", +            Color::White => "\x1b[37m", +            Color::Grey => "\x1b[38;5;7m", +        } +    } + +    const DEFAULT_FG: &'static str = "\x1b[39m"; + +    #[cfg(feature="alloc")] +    mod ansi_display_sink { +        use crate::color_new::{Color, YaxColors}; +        use crate::display::DisplaySink; + +        /// adapter to insert ANSI color command sequences in formatted text to style printed +        /// instructions. +        /// +        /// this enables similar behavior as the deprecated [`yaxpeax_arch::colors::Colorize`] trait, +        /// for outputs that can process ANSI color commands. +        /// +        /// `AnsiDisplaySink` will silently ignore errors from writes to the underlying `T: +        /// DisplaySink`. when writing to a string or other growable buffer, errors are likely +        /// inseparable from `abort()`. when writing to stdout or stderr, write failures likely +        /// mean output is piped to a process which has closed the pipe but are otherwise harmless. +        /// `span_enter_*` and `span_exit_*` don't have error reporting mechanisms in their return +        /// type, so the only available error mechanism would be to also `abort()`. +        /// +        /// if this turns out to be a bad decision, it'll have to be rethought! +        struct AnsiDisplaySink<'sink, T: DisplaySink, Y: YaxColors> { +            out: &'sink mut T, +            span_stack: alloc::vec::Vec<Color>, +            colors: Y +        } + +        impl<'sink, T: DisplaySink, Y: YaxColors> AnsiDisplaySink<'sink, T, Y> { +            fn push_color(&mut self, color: Color) { +                self.span_stack.push(color); +                let _ = self.out.write_fixed_size(super::color2ansi(color)); +            } +            fn restore_prev_color(&mut self) { +                let _ = self.span_stack.pop(); +                if let Some(prev_color) = self.span_stack.last() { +                    let _ = self.out.write_fixed_size(super::color2ansi(*prev_color)); +                } else { +                    let _ = self.out.write_fixed_size(super::DEFAULT_FG); +                }; +            } +        } + +        impl<'sink, T: DisplaySink, Y: YaxColors> core::fmt::Write for AnsiDisplaySink<'sink, T, Y> { +            fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> { +                self.out.write_str(s) +            } +            fn write_char(&mut self, c: char) -> Result<(), core::fmt::Error> { +                self.out.write_char(c) +            } +        } + +        impl<'sink, T: DisplaySink, Y: YaxColors> DisplaySink for AnsiDisplaySink<'sink, T, Y> { +            fn span_start_immediate(&mut self) { self.push_color(self.colors.number()); } +            fn span_end_immediate(&mut self) { self.restore_prev_color() } + +            fn span_start_register(&mut self) { self.push_color(self.colors.register()); } +            fn span_end_register(&mut self) { self.restore_prev_color() } + +            // ah.. the right way, currently, to colorize opcodes would be to collect text while in the +            // opcode span, and request some kind of user-provided decoder ring to translate mnemonics +            // into the right color. that's very unfortunate. maybe there should be another span for +            // `opcode_kind(u8)` for impls to report what kind of opcode they'll be emitting.. +            fn span_start_opcode(&mut self) { self.push_color(self.colors.misc_op()); } +            fn span_end_opcode(&mut self) { self.restore_prev_color() } + +            fn span_start_program_counter(&mut self) { self.push_color(self.colors.program_counter()); } +            fn span_end_program_counter(&mut self) { self.restore_prev_color() } + +            fn span_start_number(&mut self) { self.push_color(self.colors.number()); } +            fn span_end_number(&mut self) { self.restore_prev_color() } + +            fn span_start_address(&mut self) { self.push_color(self.colors.address()); } +            fn span_end_address(&mut self) { self.restore_prev_color() } + +            fn span_start_function_expr(&mut self) { self.push_color(self.colors.function()); } +            fn span_end_function_expr(&mut self) { self.restore_prev_color() } +        } +    } +    #[cfg(alloc)] +    pub use ansi_display_sink::AnsiDisplaySink; +} + +pub struct DefaultColors; + +impl YaxColors for DefaultColors { +    fn arithmetic_op(&self) -> Color { +        Color::Yellow +    } +    fn stack_op(&self) -> Color { +        Color::DarkMagenta +    } +    fn nop_op(&self) -> Color { +        Color::DarkBlue +    } +    fn stop_op(&self) -> Color { +        Color::Red +    } +    fn control_flow_op(&self) -> Color { +        Color::DarkGreen +    } +    fn data_op(&self) -> Color { +        Color::Magenta +    } +    fn comparison_op(&self) -> Color { +        Color::DarkYellow +    } +    fn invalid_op(&self) -> Color { +        Color::DarkRed +    } +    fn misc_op(&self) -> Color { +        Color::Cyan +    } +    fn platform_op(&self) -> Color { +        Color::DarkCyan +    } + +    fn register(&self) -> Color { +        Color::DarkCyan +    } +    fn program_counter(&self) -> Color { +        Color::DarkRed +    } +    fn number(&self) -> Color { +        Color::White +    } +    fn zero(&self) -> Color { +        Color::White +    } +    fn one(&self) -> Color { +        Color::White +    } +    fn minus_one(&self) -> Color { +        Color::White +    } +    fn address(&self) -> Color { +        Color::DarkGreen +    } +    fn symbol(&self) -> Color { +        Color::Green +    } +    fn function(&self) -> Color { +        Color::Green +    } +} | 
