syntect/highlighting/
style.rs

1// Code based on [https://github.com/defuz/sublimate/blob/master/src/core/syntax/scope.rs](https://github.com/defuz/sublimate/blob/master/src/core/syntax/scope.rs)
2// released under the MIT license by @defuz
3use serde_derive::{Deserialize, Serialize};
4use std::{fmt, ops};
5
6/// Foreground and background colors, with font style
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
8pub struct Style {
9    /// Foreground color
10    pub foreground: Color,
11    /// Background color
12    pub background: Color,
13    /// Style of the font
14    pub font_style: FontStyle,
15}
16
17/// A change to a [`Style`] applied incrementally by a theme rule
18///
19/// Fields left empty (as `None`) will not modify the corresponding field on a `Style`
20///
21/// [`Style`]: struct.Style.html
22#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
23pub struct StyleModifier {
24    /// Foreground color
25    pub foreground: Option<Color>,
26    /// Background color
27    pub background: Option<Color>,
28    /// Style of the font
29    pub font_style: Option<FontStyle>,
30}
31
32/// RGBA color, directly from the theme
33///
34/// Because these numbers come directly from the theme, you might have to do your own color space
35/// conversion if you're outputting a different color space from the theme. This can be a problem
36/// because some Sublime themes use sRGB and some don't. This is specified in an attribute syntect
37/// doesn't parse yet.
38#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
39pub struct Color {
40    /// Red component
41    pub r: u8,
42    /// Green component
43    pub g: u8,
44    /// Blue component
45    pub b: u8,
46    /// Alpha (transparency) component
47    pub a: u8,
48}
49
50// More compact alternate debug representation by not using a separate line for each color field,
51// also adapts the default debug representation to match.
52impl std::fmt::Debug for Color {
53    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
54        let Color { r, g, b, a } = self;
55        if f.alternate() {
56            // when formatted with "{:#?}"
57            write!(
58                f,
59                "Color {{ r/g/b/a: {: >3}/{: >3}/{: >3}/{: >3} }}",
60                r, g, b, a
61            )
62        } else {
63            // when formatted with "{:?}"
64            write!(f, "Color {{ r/g/b/a: {}/{}/{}/{} }}", r, g, b, a)
65        }
66    }
67}
68
69/// The color-independent styling of a font - i.e. bold, italicized, and/or underlined
70#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
71pub struct FontStyle {
72    bits: u8,
73}
74
75impl FontStyle {
76    /// Bold font style
77    pub const BOLD: Self = Self { bits: 1 };
78    /// Underline font style
79    pub const UNDERLINE: Self = Self { bits: 2 };
80    /// Italic font style
81    pub const ITALIC: Self = Self { bits: 4 };
82
83    /// Returns an empty set of flags.
84    pub const fn empty() -> Self {
85        Self { bits: 0 }
86    }
87
88    /// Returns the set containing all flags.
89    pub const fn all() -> Self {
90        let bits = Self::BOLD.bits | Self::UNDERLINE.bits | Self::ITALIC.bits;
91        Self { bits }
92    }
93
94    /// Returns the raw value of the flags currently stored.
95    pub const fn bits(&self) -> u8 {
96        self.bits
97    }
98
99    /// Convert from underlying bit representation, unless that
100    /// representation contains bits that do not correspond to a flag.
101    pub const fn from_bits(bits: u8) -> Option<Self> {
102        if (bits & !Self::all().bits()) == 0 {
103            Some(Self { bits })
104        } else {
105            None
106        }
107    }
108
109    /// Convert from underlying bit representation, dropping any bits
110    /// that do not correspond to flags.
111    pub const fn from_bits_truncate(bits: u8) -> Self {
112        let bits = bits & Self::all().bits;
113        Self { bits }
114    }
115
116    /// Convert from underlying bit representation, preserving all
117    /// bits (even those not corresponding to a defined flag).
118    ///
119    /// # Safety
120    ///
121    /// The caller of the `bitflags!` macro can chose to allow or
122    /// disallow extra bits for their bitflags type.
123    ///
124    /// The caller of `from_bits_unchecked()` has to ensure that
125    /// all bits correspond to a defined flag or that extra bits
126    /// are valid for this bitflags type.
127    pub const unsafe fn from_bits_unchecked(bits: u8) -> Self {
128        Self { bits }
129    }
130
131    /// Returns `true` if no flags are currently stored.
132    pub const fn is_empty(&self) -> bool {
133        self.bits() == Self::empty().bits()
134    }
135
136    /// Returns `true` if all flags are currently set.
137    pub const fn is_all(&self) -> bool {
138        self.bits() == Self::all().bits()
139    }
140
141    /// Returns `true` if there are flags common to both `self` and `other`.
142    pub const fn intersects(&self, other: Self) -> bool {
143        let bits = self.bits & other.bits;
144        !(Self { bits }).is_empty()
145    }
146
147    /// Returns `true` if all of the flags in `other` are contained within `self`.
148    pub const fn contains(&self, other: Self) -> bool {
149        (self.bits & other.bits) == other.bits
150    }
151
152    /// Inserts the specified flags in-place.
153    pub fn insert(&mut self, other: Self) {
154        self.bits |= other.bits;
155    }
156
157    /// Removes the specified flags in-place.
158    pub fn remove(&mut self, other: Self) {
159        self.bits &= !other.bits;
160    }
161
162    /// Toggles the specified flags in-place.
163    pub fn toggle(&mut self, other: Self) {
164        self.bits ^= other.bits;
165    }
166
167    /// Inserts or removes the specified flags depending on the passed value.
168    pub fn set(&mut self, other: Self, value: bool) {
169        if value {
170            self.insert(other);
171        } else {
172            self.remove(other);
173        }
174    }
175
176    /// Returns the intersection between the flags in `self` and
177    /// `other`.
178    ///
179    /// Specifically, the returned set contains only the flags which are
180    /// present in *both* `self` *and* `other`.
181    ///
182    /// This is equivalent to using the `&` operator (e.g.
183    /// [`ops::BitAnd`]), as in `flags & other`.
184    ///
185    /// [`ops::BitAnd`]: https://doc.rust-lang.org/std/ops/trait.BitAnd.html
186    #[must_use]
187    pub const fn intersection(self, other: Self) -> Self {
188        let bits = self.bits & other.bits;
189        Self { bits }
190    }
191
192    /// Returns the union of between the flags in `self` and `other`.
193    ///
194    /// Specifically, the returned set contains all flags which are
195    /// present in *either* `self` *or* `other`, including any which are
196    /// present in both (see [`Self::symmetric_difference`] if that
197    /// is undesirable).
198    ///
199    /// This is equivalent to using the `|` operator (e.g.
200    /// [`ops::BitOr`]), as in `flags | other`.
201    ///
202    /// [`ops::BitOr`]: https://doc.rust-lang.org/std/ops/trait.BitOr.html
203    #[must_use]
204    pub const fn union(self, other: Self) -> Self {
205        let bits = self.bits | other.bits;
206        Self { bits }
207    }
208
209    /// Returns the difference between the flags in `self` and `other`.
210    ///
211    /// Specifically, the returned set contains all flags present in
212    /// `self`, except for the ones present in `other`.
213    ///
214    /// It is also conceptually equivalent to the "bit-clear" operation:
215    /// `flags & !other` (and this syntax is also supported).
216    ///
217    /// This is equivalent to using the `-` operator (e.g.
218    /// [`ops::Sub`]), as in `flags - other`.
219    ///
220    /// [`ops::Sub`]: https://doc.rust-lang.org/std/ops/trait.Sub.html
221    pub const fn difference(self, other: Self) -> Self {
222        let bits = self.bits & !other.bits;
223        Self { bits }
224    }
225
226    /// Returns the [symmetric difference][sym-diff] between the flags
227    /// in `self` and `other`.
228    ///
229    /// Specifically, the returned set contains the flags present which
230    /// are present in `self` or `other`, but that are not present in
231    /// both. Equivalently, it contains the flags present in *exactly
232    /// one* of the sets `self` and `other`.
233    ///
234    /// This is equivalent to using the `^` operator (e.g.
235    /// [`ops::BitXor`]), as in `flags ^ other`.
236    ///
237    /// [sym-diff]: https://en.wikipedia.org/wiki/Symmetric_difference
238    /// [`ops::BitXor`]: https://doc.rust-lang.org/std/ops/trait.BitXor.html
239    #[must_use]
240    pub const fn symmetric_difference(self, other: Self) -> Self {
241        let bits = self.bits ^ other.bits;
242        Self { bits }
243    }
244
245    /// Returns the complement of this set of flags.
246    ///
247    /// Specifically, the returned set contains all the flags which are
248    /// not set in `self`, but which are allowed for this type.
249    ///
250    /// Alternatively, it can be thought of as the set difference
251    /// between [`Self::all()`] and `self` (e.g. `Self::all() - self`)
252    ///
253    /// This is equivalent to using the `!` operator (e.g.
254    /// [`ops::Not`]), as in `!flags`.
255    ///
256    /// [`Self::all()`]: Self::all
257    /// [`ops::Not`]: https://doc.rust-lang.org/std/ops/trait.Not.html
258    #[must_use]
259    pub const fn complement(self) -> Self {
260        Self::from_bits_truncate(!self.bits)
261    }
262}
263
264impl fmt::Debug for FontStyle {
265    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
266        let mut empty = true;
267
268        let pairs = [
269            (Self::BOLD, "BOLD"),
270            (Self::UNDERLINE, "UNDERLINE"),
271            (Self::ITALIC, "ITALIC"),
272        ];
273        for (flag, flag_str) in pairs {
274            if self.contains(flag) {
275                if !std::mem::take(&mut empty) {
276                    f.write_str(" | ")?;
277                }
278                f.write_str(flag_str)?;
279            }
280        }
281
282        let extra_bits = self.bits & !Self::all().bits();
283        if extra_bits != 0 {
284            if !std::mem::take(&mut empty) {
285                f.write_str(" | ")?;
286            }
287            f.write_str("0x")?;
288            fmt::LowerHex::fmt(&extra_bits, f)?;
289        }
290
291        if empty {
292            f.write_str("(empty)")?;
293        }
294
295        Ok(())
296    }
297}
298
299impl fmt::Binary for FontStyle {
300    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
301        fmt::Binary::fmt(&self.bits, f)
302    }
303}
304
305impl fmt::Octal for FontStyle {
306    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
307        fmt::Octal::fmt(&self.bits, f)
308    }
309}
310
311impl fmt::LowerHex for FontStyle {
312    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
313        fmt::LowerHex::fmt(&self.bits, f)
314    }
315}
316
317impl fmt::UpperHex for FontStyle {
318    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
319        fmt::UpperHex::fmt(&self.bits, f)
320    }
321}
322
323impl ops::BitOr for FontStyle {
324    type Output = Self;
325    /// Returns the union of the two sets of flags.
326    fn bitor(self, other: FontStyle) -> Self {
327        let bits = self.bits | other.bits;
328        Self { bits }
329    }
330}
331
332impl ops::BitOrAssign for FontStyle {
333    /// Adds the set of flags.
334    fn bitor_assign(&mut self, other: Self) {
335        self.bits |= other.bits;
336    }
337}
338
339impl ops::BitXor for FontStyle {
340    type Output = Self;
341    /// Returns the left flags, but with all the right flags toggled.
342    fn bitxor(self, other: Self) -> Self {
343        let bits = self.bits ^ other.bits;
344        Self { bits }
345    }
346}
347
348impl ops::BitXorAssign for FontStyle {
349    /// Toggles the set of flags.
350    fn bitxor_assign(&mut self, other: Self) {
351        self.bits ^= other.bits;
352    }
353}
354
355impl ops::BitAnd for FontStyle {
356    type Output = Self;
357    /// Returns the intersection between the two sets of flags.
358    fn bitand(self, other: Self) -> Self {
359        let bits = self.bits & other.bits;
360        Self { bits }
361    }
362}
363
364impl ops::BitAndAssign for FontStyle {
365    /// Disables all flags disabled in the set.
366    fn bitand_assign(&mut self, other: Self) {
367        self.bits &= other.bits;
368    }
369}
370
371impl ops::Sub for FontStyle {
372    type Output = Self;
373    /// Returns the set difference of the two sets of flags.
374    fn sub(self, other: Self) -> Self {
375        let bits = self.bits & !other.bits;
376        Self { bits }
377    }
378}
379
380impl ops::SubAssign for FontStyle {
381    /// Disables all flags enabled in the set.
382    fn sub_assign(&mut self, other: Self) {
383        self.bits &= !other.bits;
384    }
385}
386
387impl ops::Not for FontStyle {
388    type Output = Self;
389    /// Returns the complement of this set of flags.
390    fn not(self) -> Self {
391        Self { bits: !self.bits } & Self::all()
392    }
393}
394
395impl Extend<FontStyle> for FontStyle {
396    fn extend<T: IntoIterator<Item = Self>>(&mut self, iterator: T) {
397        for item in iterator {
398            self.insert(item)
399        }
400    }
401}
402
403impl FromIterator<FontStyle> for FontStyle {
404    fn from_iter<T: IntoIterator<Item = Self>>(iterator: T) -> Self {
405        let mut result = Self::empty();
406        result.extend(iterator);
407        result
408    }
409}
410
411impl Color {
412    /// The color black (`#000000`)
413    pub const BLACK: Color = Color {
414        r: 0x00,
415        g: 0x00,
416        b: 0x00,
417        a: 0xFF,
418    };
419
420    /// The color white (`#FFFFFF`)
421    pub const WHITE: Color = Color {
422        r: 0xFF,
423        g: 0xFF,
424        b: 0xFF,
425        a: 0xFF,
426    };
427}
428
429impl Style {
430    /// Applies a change to this style, yielding a new changed style
431    pub fn apply(&self, modifier: StyleModifier) -> Style {
432        Style {
433            foreground: modifier.foreground.unwrap_or(self.foreground),
434            background: modifier.background.unwrap_or(self.background),
435            font_style: modifier.font_style.unwrap_or(self.font_style),
436        }
437    }
438}
439
440impl Default for Style {
441    fn default() -> Style {
442        Style {
443            foreground: Color::BLACK,
444            background: Color::WHITE,
445            font_style: FontStyle::empty(),
446        }
447    }
448}
449
450impl StyleModifier {
451    /// Applies the other modifier to this one, creating a new modifier.
452    ///
453    /// Values in `other` are preferred.
454    pub fn apply(&self, other: StyleModifier) -> StyleModifier {
455        StyleModifier {
456            foreground: other.foreground.or(self.foreground),
457            background: other.background.or(self.background),
458            font_style: other.font_style.or(self.font_style),
459        }
460    }
461}