css_lexer/
source_cursor.rs1use crate::{
2 AssociatedWhitespaceRules, CommentStyle, Cursor, Kind, KindSet, QuoteStyle, SourceOffset, Span, ToSpan, Token,
3};
4use std::fmt::{Display, Formatter, Result};
5
6#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
8pub struct SourceCursor<'a> {
9 cursor: Cursor,
10 source: &'a str,
11}
12
13impl<'a> ToSpan for SourceCursor<'a> {
14 fn to_span(&self) -> Span {
15 self.cursor.to_span()
16 }
17}
18
19impl<'a> Display for SourceCursor<'a> {
20 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
21 match self.token().kind() {
22 Kind::Eof => Ok(()),
23 Kind::String => match self.token().quote_style() {
27 QuoteStyle::Single => {
28 let inner =
29 &self.source[1..(self.token().len() as usize) - self.token().has_close_quote() as usize];
30 write!(f, "'{inner}'")
31 }
32 QuoteStyle::Double => {
33 let inner =
34 &self.source[1..(self.token().len() as usize) - self.token().has_close_quote() as usize];
35 write!(f, "\"{inner}\"")
36 }
37 QuoteStyle::None => unreachable!(),
39 },
40 Kind::Delim
41 | Kind::Colon
42 | Kind::Semicolon
43 | Kind::Comma
44 | Kind::LeftSquare
45 | Kind::LeftParen
46 | Kind::RightSquare
47 | Kind::RightParen
48 | Kind::LeftCurly
49 | Kind::RightCurly => self.token().char().unwrap().fmt(f),
50 _ => f.write_str(self.source),
51 }
52 }
53}
54
55impl<'a> SourceCursor<'a> {
56 pub const SPACE: SourceCursor<'static> = SourceCursor::from(Cursor::new(SourceOffset(0), Token::SPACE), " ");
57 pub const TAB: SourceCursor<'static> = SourceCursor::from(Cursor::new(SourceOffset(0), Token::TAB), "\t");
58 pub const NEWLINE: SourceCursor<'static> = SourceCursor::from(Cursor::new(SourceOffset(0), Token::NEWLINE), "\n");
59
60 #[inline(always)]
61 pub const fn from(cursor: Cursor, source: &'a str) -> Self {
62 debug_assert!(
63 (cursor.len() as usize) == source.len(),
64 "A SourceCursor should be constructed with a source that matches the length of the cursor!"
65 );
66 Self { cursor, source }
67 }
68
69 #[inline(always)]
70 pub const fn cursor(&self) -> Cursor {
71 self.cursor
72 }
73
74 #[inline(always)]
75 pub const fn token(&self) -> Token {
76 self.cursor.token()
77 }
78
79 #[inline(always)]
80 pub const fn source(&self) -> &'a str {
81 self.source
82 }
83
84 pub fn with_quotes(&self, quote_style: QuoteStyle) -> Self {
85 Self::from(self.cursor.with_quotes(quote_style), self.source)
86 }
87
88 pub fn with_associated_whitespace(&self, rules: AssociatedWhitespaceRules) -> Self {
89 Self::from(self.cursor.with_associated_whitespace(rules), self.source)
90 }
91}
92
93impl PartialEq<Kind> for SourceCursor<'_> {
94 fn eq(&self, other: &Kind) -> bool {
95 self.token() == *other
96 }
97}
98
99impl PartialEq<CommentStyle> for SourceCursor<'_> {
100 fn eq(&self, other: &CommentStyle) -> bool {
101 self.token() == *other
102 }
103}
104
105impl From<SourceCursor<'_>> for KindSet {
106 fn from(cursor: SourceCursor<'_>) -> Self {
107 cursor.token().into()
108 }
109}
110
111impl PartialEq<KindSet> for SourceCursor<'_> {
112 fn eq(&self, other: &KindSet) -> bool {
113 self.token() == *other
114 }
115}