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 + } +} |