css_lexer/comment_style.rs
1/// An enum representing the "Style" the [Kind::Comment][crate::Kind::Comment] token represents.
2///
3/// A [Token][crate::Token] with [Kind::Comment][crate::Kind::Comment] will store this data internal to the token.
4/// Using [Token::comment_style()][crate::Token::comment_style()] will return this enum, depending on what characters
5/// make up the beginning of the comment token. By default the [Lexer][crate::Lexer] will only produce multi-line - aka
6/// "Block" - comments, but adding [Feature::SeparateWhitespace][crate::Feature::SingleLineComments] will allow the
7/// [Lexer][crate::Lexer] to produce single line comments too.
8///
9/// A basic [Block][CommentStyle::Block] comment style uses the `/*` leading characters, but sub-styles of the block
10/// style are also computed, for example [BlockStar][CommentStyle::BlockStar] represents a comment using the "double
11/// star" syntax to open the comment, i.e. `/**`. Determing if these comments are using these alternate style can help a
12/// parser (or writer) determine if it should retain these comments or otherwise treat them differently to regular block
13/// comments.
14///
15/// ```
16/// use css_lexer::*;
17/// let mut lexer = Lexer::new("/* Normal Comment */ /** Double Star Comment */");
18/// {
19/// // This token will be collapsed Whitespace.
20/// let token = lexer.advance();
21/// assert_eq!(token, Kind::Comment);
22/// assert_eq!(token, CommentStyle::Block);
23/// }
24/// assert_eq!(lexer.advance(), Kind::Whitespace);
25/// {
26/// // This token will be collapsed Whitespace.
27/// let token = lexer.advance();
28/// assert_eq!(token, Kind::Comment);
29/// assert_eq!(token, CommentStyle::BlockStar);
30/// }
31/// ```
32#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
33#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(tag = "kind", content = "value"))]
34pub enum CommentStyle {
35 #[default]
36 /// A basic block comment which uses `/*` as the leading style. The third character may be a whitespace, or may
37 /// include a character that _isn't_ `!`, `#`, `=`, `-`.
38 Block = 0b000,
39 /// A block comment which uses `/**` as the leading style. The two `*`s must be adjacent, so this does not count
40 /// `/* *`.
41 BlockStar = 0b001,
42 /// A block comment which uses `/*!` as the leading style. The `*` and `!` must be adjacent, so this does not count
43 /// `/* !`.
44 BlockBang = 0b010,
45 /// A block comment which uses `/*#` as the leading style. The `*` and `#` must be adjacent, so this does not count
46 /// `/* #`.
47 BlockPound = 0b011,
48 /// A block comment which uses `/*=` or `/*-` as the leading style. The `*` and `-` or `=` must be adjacent, so this
49 /// does not count `/* #`.
50 BlockHeading = 0b100,
51 /// A basic single line comment which uses `//` as the leading style. The third character may be a whitespace, or
52 /// may include a character that _isn't_ `*`, `!`. The [Lexer][crate::Lexer] can only produce a [Token][crate::Token]
53 /// with this style if [Feature::SingleLineComments][crate::Feature::SingleLineComments] is enabled.
54 Single = 0b101,
55 /// A single line comment which uses `//*` as the leading style. The `*` be adjacent to the `//`, so this does not
56 /// count `// *`. The [Lexer][crate::Lexer] can only produce a [Token][crate::Token] with this style if
57 /// [Feature::SingleLineComments][crate::Feature::SingleLineComments] is enabled.
58 SingleStar = 0b110,
59 /// A single line comment which uses `//!` as the leading style. The `!` be adjacent to the `//`, so this does not
60 /// count `// !`. The [Lexer][crate::Lexer] can only produce a [Token][crate::Token] with this style if
61 /// [Feature::SingleLineComments][crate::Feature::SingleLineComments] is enabled.
62 SingleBang = 0b111,
63}
64
65impl CommentStyle {
66 #[inline]
67 pub fn is_block(&self) -> bool {
68 matches!(self, Self::Block | Self::BlockStar | Self::BlockBang | Self::BlockPound | Self::BlockHeading)
69 }
70
71 #[inline]
72 pub fn is_non_standard(&self) -> bool {
73 matches!(self, Self::Single | Self::SingleStar | Self::SingleBang)
74 }
75
76 #[inline]
77 pub fn retain(&self) -> bool {
78 matches!(self, Self::Single | Self::SingleStar | Self::SingleBang)
79 }
80
81 pub(crate) fn from_bits(bits: u8) -> Option<Self> {
82 match bits {
83 0b000 => Some(Self::Block),
84 0b001 => Some(Self::BlockStar),
85 0b010 => Some(Self::BlockBang),
86 0b011 => Some(Self::BlockPound),
87 0b100 => Some(Self::BlockHeading),
88 0b101 => Some(Self::Single),
89 0b110 => Some(Self::SingleStar),
90 0b111 => Some(Self::SingleBang),
91 _ => None,
92 }
93 }
94}
95
96#[test]
97fn size_test() {
98 assert_eq!(::std::mem::size_of::<CommentStyle>(), 1);
99}
100
101#[test]
102fn test_from_bits() {
103 assert_eq!(CommentStyle::from_bits(CommentStyle::Block as u8), Some(CommentStyle::Block));
104 assert_eq!(CommentStyle::from_bits(CommentStyle::BlockStar as u8), Some(CommentStyle::BlockStar));
105 assert_eq!(CommentStyle::from_bits(CommentStyle::BlockBang as u8), Some(CommentStyle::BlockBang));
106 assert_eq!(CommentStyle::from_bits(CommentStyle::BlockPound as u8), Some(CommentStyle::BlockPound));
107 assert_eq!(CommentStyle::from_bits(CommentStyle::BlockHeading as u8), Some(CommentStyle::BlockHeading));
108 assert_eq!(CommentStyle::from_bits(CommentStyle::Single as u8), Some(CommentStyle::Single));
109 assert_eq!(CommentStyle::from_bits(CommentStyle::SingleStar as u8), Some(CommentStyle::SingleStar));
110 assert_eq!(CommentStyle::from_bits(CommentStyle::SingleBang as u8), Some(CommentStyle::SingleBang));
111}