any_lexer/lexers/
scss.rs

1use text_scanner::{ext::ScssScannerExt, Scanner};
2
3use crate::{impl_lexer_from_scanner, CssToken, ScanToken, ScannerExt, TokenSpan};
4
5#[derive(PartialEq, Eq, Clone, Copy, Debug)]
6pub enum ScssToken {
7    Space,
8    LineComment,
9    BlockComment,
10    Ident,
11    AtKeyword,
12    Hash,
13    String,
14    Number,
15    /// Punctuation e.g. `:`, `,`.
16    Punct,
17    /// Delimiter e.g. `{`, `}`, `[`, and `]`.
18    Delim,
19    /// Given valid SCSS, then this variant should never be encountered. If is
20    /// is encountered, then check if an issue has already been submitted,
21    /// otherwise please [submit an issue].
22    ///
23    /// [submit an issue]: https://github.com/vallentin/colorblast/issues
24    Unknown,
25}
26
27impl ScanToken for ScssToken {
28    fn scan_token<'text>(scanner: &mut Scanner<'text>) -> Option<(Self, TokenSpan<'text>)> {
29        if let Ok((r, _s)) = scanner.scan_scss_line_comment() {
30            return Some((Self::LineComment, scanner.span(r)));
31        }
32
33        let (tok, span) = CssToken::scan_token(scanner)?;
34        match tok {
35            CssToken::Space => Some((ScssToken::Space, span)),
36            CssToken::BlockComment => Some((ScssToken::BlockComment, span)),
37            CssToken::Ident => Some((ScssToken::Ident, span)),
38            CssToken::AtKeyword => Some((ScssToken::AtKeyword, span)),
39            CssToken::Hash => Some((ScssToken::Hash, span)),
40            CssToken::String => Some((ScssToken::String, span)),
41            CssToken::Number => Some((ScssToken::Number, span)),
42            CssToken::Punct => Some((ScssToken::Punct, span)),
43            CssToken::Delim => Some((ScssToken::Delim, span)),
44            CssToken::Unknown => Some((ScssToken::Unknown, span)),
45        }
46    }
47}
48
49/// SCSS lexer producing [`ScssToken`]s.
50///
51/// **Note:** Cloning `ScssLexer` is essentially a copy, as it just contains
52/// a `&str` and a `usize` for its `cursor`. However, `Copy` is not
53/// implemented, to avoid accidentally copying immutable `ScssLexer`s.
54///
55/// See also [`CssLexer`].
56///
57/// [`CssLexer`]: super::CssLexer
58#[derive(Clone, Debug)]
59pub struct ScssLexer<'text> {
60    scanner: Scanner<'text>,
61}
62
63impl<'text> ScssLexer<'text> {
64    #[inline]
65    pub fn new(text: &'text str) -> Self {
66        Self {
67            scanner: Scanner::new(text),
68        }
69    }
70}
71
72impl_lexer_from_scanner!('text, ScssLexer<'text>, ScssToken, scanner);
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77
78    #[test]
79    fn test_scss_lexer_spans() {
80        // This intentionally uses Rust code as input, as it is
81        // only testing that ScssLexer returns all characters
82        let input = include_str!("../../../text-scanner/src/ext/rust.rs");
83        let mut output = String::new();
84
85        let lexer = ScssLexer::new(input);
86        for (_tok, span) in lexer {
87            output.push_str(span.as_str());
88        }
89
90        assert_eq!(input, output);
91    }
92}