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}