aboutsummaryrefslogtreecommitdiff
path: root/src/color_new.rs
blob: 482a6242940f035fbaf97e90a92d0f6c71fea8cd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
#[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",
        }
    }

    // could reasonably be always present, but only used if feature="alloc"
    #[cfg(feature="alloc")]
    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(feature="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
    }
}