css_parse/
cursor_overlay_sink.rs

1use crate::{Cursor, CursorSink, Kind, SourceCursor, SourceCursorSink, SourceOffset, Span, ToSpan};
2use bumpalo::{Bump, collections::Vec};
3use std::collections::BTreeMap;
4
5#[derive(Debug)]
6pub struct OverlaySegment<'a> {
7	span: Span,
8	cursors: Vec<'a, SourceCursor<'a>>,
9	kind: OverlayKind,
10}
11
12impl<'a> OverlaySegment<'a> {
13	pub fn new(span: Span, cursors: Vec<'a, SourceCursor<'a>>, kind: OverlayKind) -> Self {
14		Self { span, cursors, kind }
15	}
16
17	pub fn start(&self) -> SourceOffset {
18		self.span.start()
19	}
20
21	pub fn end(&self) -> SourceOffset {
22		self.span.end()
23	}
24
25	pub fn cursors(&self) -> &[SourceCursor<'a>] {
26		&self.cursors
27	}
28
29	pub fn is_insertion(&self) -> bool {
30		matches!(self.kind, OverlayKind::InsertBefore | OverlayKind::InsertAfter)
31	}
32
33	pub fn kind(&self) -> OverlayKind {
34		self.kind
35	}
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
39pub enum OverlayKind {
40	Replace,
41	InsertBefore,
42	InsertAfter,
43}
44
45#[derive(Debug)]
46pub struct CursorOverlaySet<'a> {
47	segments: Vec<'a, OverlaySegment<'a>>,
48}
49
50impl<'a> CursorOverlaySet<'a> {
51	pub fn new(bump: &'a Bump) -> Self {
52		Self { segments: Vec::new_in(bump) }
53	}
54
55	pub fn insert(&mut self, span: Span, cursors: Vec<'a, SourceCursor<'a>>) {
56		#[cfg(debug_assertions)]
57		{
58			let has_non_eof = cursors.iter().any(|cursor| cursor.token() != Kind::Eof);
59			debug_assert!(has_non_eof || cursors.is_empty(), "Overlay for span {:?} produced no output", span);
60		}
61		self.push_segment(OverlaySegment::new(span, cursors, OverlayKind::Replace));
62	}
63
64	pub fn clear(&mut self) {
65		self.segments.clear();
66	}
67
68	pub fn has_overlay(&self, span: Span) -> bool {
69		let start = &span.start();
70		let end = &span.end();
71		self.segments
72			.iter()
73			.any(|segment| !segment.is_insertion() && segment.start() <= *start && *end <= segment.end())
74	}
75
76	pub fn push_segment(&mut self, segment: OverlaySegment<'a>) {
77		let idx = self.segments.partition_point(|existing| {
78			existing.start() < segment.start()
79				|| (existing.start() == segment.start() && existing.end() <= segment.end())
80		});
81		self.segments.insert(idx, segment);
82	}
83
84	pub fn segments(&self) -> &[OverlaySegment<'a>] {
85		&self.segments
86	}
87}
88
89/// This is a [CursorSink] that wraps a [SourceCursorSink], while also taking a [CursorOverlaySet]. As [Cursor]s get
90/// appended into this sink, it will replay those to the underlying [SourceCursorSink] _unless_ a [CursorOverlaySet]
91/// overlaps the [Cursor]'s span, at which point the overlay wil be replayed to the underlying [SourceCursorSink].
92/// This Sink is useful for collecting new Cursors (say from an AST) to overlap (or, say, transform) the underlying base
93/// Cursors (read: AST). In other words, writing over the top of the source.
94///
95/// Other than replaying overlays in place of the underyling cursors, no other modifications are made to the Cursors,
96/// that is up to the base SourceCursorSink, which can apply additional formatting or logic.
97#[derive(Debug)]
98pub struct CursorOverlaySink<'a, 'o, T: SourceCursorSink<'a>> {
99	source_text: &'a str,
100	overlays: &'o CursorOverlaySet<'a>,
101	sink: T,
102	processed_overlay_ranges: BTreeMap<SourceOffset, SourceOffset>,
103	next_segment: usize,
104	#[cfg(debug_assertions)]
105	seen_eof: bool,
106}
107
108impl<'a, 'o, T: SourceCursorSink<'a>> CursorOverlaySink<'a, 'o, T> {
109	pub fn new(source_text: &'a str, overlays: &'o CursorOverlaySet<'a>, sink: T) -> Self {
110		Self {
111			source_text,
112			overlays,
113			sink,
114			processed_overlay_ranges: BTreeMap::new(),
115			next_segment: 0,
116			#[cfg(debug_assertions)]
117			seen_eof: false,
118		}
119	}
120
121	fn flush_segments_up_to(&mut self, limit: SourceOffset, include_after: bool) {
122		let segments = self.overlays.segments();
123		while self.next_segment < segments.len() {
124			let segment = &segments[self.next_segment];
125			if segment.start() > limit {
126				break;
127			}
128			if !include_after && segment.kind() == OverlayKind::InsertAfter && segment.start() == limit {
129				break;
130			}
131			for cursor in segment.cursors() {
132				if cursor.token() != Kind::Eof {
133					self.sink.append(*cursor);
134				}
135			}
136			if !segment.is_insertion() {
137				self.processed_overlay_ranges.insert(segment.start(), segment.end());
138			}
139			self.next_segment += 1;
140		}
141	}
142
143	fn cursor_is_consumed(&self, cursor_start: SourceOffset, cursor_end: SourceOffset) -> bool {
144		self.processed_overlay_ranges
145			.range(..=cursor_start)
146			.next_back()
147			.is_some_and(|(&range_start, &range_end)| cursor_start >= range_start && cursor_end <= range_end)
148	}
149}
150
151impl<'a, 'o, T: SourceCursorSink<'a>> SourceCursorSink<'a> for CursorOverlaySink<'a, 'o, T> {
152	fn append(&mut self, c: SourceCursor<'a>) {
153		let cursor_start = c.to_span().start();
154		let cursor_end = c.to_span().end();
155
156		self.flush_segments_up_to(cursor_start, false);
157
158		if self.cursor_is_consumed(cursor_start, cursor_end) {
159			return;
160		}
161
162		self.sink.append(c);
163
164		self.flush_segments_up_to(cursor_end, true);
165	}
166}
167
168impl<'a, 'o, T: SourceCursorSink<'a>> CursorSink for CursorOverlaySink<'a, 'o, T> {
169	fn append(&mut self, c: Cursor) {
170		#[cfg(debug_assertions)]
171		{
172			debug_assert!(!self.seen_eof, "Received cursor after EOF: {:?}", c);
173			if c == Kind::Eof {
174				self.seen_eof = true;
175			}
176		}
177
178		SourceCursorSink::append(self, SourceCursor::from(c, c.str_slice(self.source_text)))
179	}
180}
181
182#[cfg(test)]
183mod test {
184	use super::*;
185	use crate::{
186		ComponentValue, ComponentValues, CursorPrettyWriteSink, CursorToSourceCursorSink, CursorWriteSink,
187		EmptyAtomSet, Parser, QuoteStyle, T, ToCursors, ToSpan,
188	};
189	use bumpalo::{Bump, collections::Vec};
190	use css_lexer::Lexer;
191
192	fn snippet_cursors<'a>(bump: &'a Bump, snippet: &'a str) -> Vec<'a, SourceCursor<'a>> {
193		let lexer = Lexer::new(&EmptyAtomSet::ATOMS, snippet);
194		let mut parser = Parser::new(bump, snippet, lexer);
195		let parsed = parser.parse_entirely::<ComponentValues<'a>>();
196		let mut cursors = Vec::new_in(bump);
197		let mut sink = CursorToSourceCursorSink::new(snippet, &mut cursors);
198		parsed.to_cursors(&mut sink);
199		cursors
200	}
201
202	#[test]
203	fn test_basic() {
204		let source_text = "black white";
205		let bump = Bump::default();
206		let lexer = Lexer::new(&EmptyAtomSet::ATOMS, source_text);
207		let mut p = Parser::new(&bump, source_text, lexer);
208		let output = p.parse_entirely::<(T![Ident], T![Ident])>().output.unwrap();
209
210		let overlay_text = "green";
211		let lexer = Lexer::new(&EmptyAtomSet::ATOMS, overlay_text);
212		let mut p = Parser::new(&bump, overlay_text, lexer);
213		let overlay = p.parse_entirely::<T![Ident]>();
214		let mut source_cursors = Vec::new_in(&bump);
215		let mut sink = CursorToSourceCursorSink::new(overlay_text, &mut source_cursors);
216		overlay.to_cursors(&mut sink);
217		let mut overlays = CursorOverlaySet::new(&bump);
218		overlays.insert(output.1.to_span(), source_cursors);
219
220		let mut str = String::new();
221		let mut stream = CursorOverlaySink::new(source_text, &overlays, CursorWriteSink::new(source_text, &mut str));
222		output.to_cursors(&mut stream);
223
224		assert_eq!(str, "black green");
225	}
226
227	#[test]
228	fn test_with_pretty_writer() {
229		let source_text = "foo{use:other;}";
230		let bump = Bump::default();
231		let lexer = Lexer::new(&EmptyAtomSet::ATOMS, source_text);
232		let mut p = Parser::new(&bump, source_text, lexer);
233		let output = p.parse_entirely::<Vec<'_, ComponentValue>>().output.unwrap();
234		let ComponentValue::SimpleBlock(ref block) = output[1] else { panic!("output[1] was not a block") };
235
236		let overlay_text = "inner{foo: bar;}";
237		let lexer = Lexer::new(&EmptyAtomSet::ATOMS, overlay_text);
238		let mut p = Parser::new(&bump, overlay_text, lexer);
239		let overlay = p.parse_entirely::<Vec<'_, ComponentValue>>();
240		let mut source_cursors = Vec::new_in(&bump);
241		let mut sink = CursorToSourceCursorSink::new(overlay_text, &mut source_cursors);
242		overlay.to_cursors(&mut sink);
243		let mut overlays = CursorOverlaySet::new(&bump);
244		overlays.insert(block.values.to_span(), source_cursors);
245
246		let mut str = String::new();
247		let mut stream = CursorOverlaySink::new(
248			source_text,
249			&overlays,
250			CursorPrettyWriteSink::new(source_text, &mut str, None, QuoteStyle::Double),
251		);
252		output.to_cursors(&mut stream);
253
254		assert_eq!(
255			str,
256			r#"
257foo {
258	inner {
259		foo: bar;
260	}
261}
262			"#
263			.trim()
264		);
265	}
266
267	#[test]
268	fn test_insert_before_and_after() {
269		let source_text = "ab";
270		let bump = Bump::default();
271		let lexer = Lexer::new(&EmptyAtomSet::ATOMS, source_text);
272		let mut parser = Parser::new(&bump, source_text, lexer);
273		let output = parser.parse_entirely::<Vec<'_, ComponentValue>>().output.unwrap();
274
275		let mut overlays = CursorOverlaySet::new(&bump);
276		overlays.push_segment(OverlaySegment::new(
277			Span::new(SourceOffset(0), SourceOffset(0)),
278			snippet_cursors(&bump, "pre"),
279			OverlayKind::InsertBefore,
280		));
281		overlays.push_segment(OverlaySegment::new(
282			Span::new(SourceOffset(2), SourceOffset(2)),
283			snippet_cursors(&bump, "post"),
284			OverlayKind::InsertAfter,
285		));
286
287		let mut str = String::new();
288		let mut stream = CursorOverlaySink::new(source_text, &overlays, CursorWriteSink::new(source_text, &mut str));
289		output.to_cursors(&mut stream);
290		assert_eq!(str, "pre ab post");
291	}
292
293	#[test]
294	fn test_multiple_inserts_preserve_order() {
295		let source_text = "x";
296		let bump = Bump::default();
297		let lexer = Lexer::new(&EmptyAtomSet::ATOMS, source_text);
298		let mut parser = Parser::new(&bump, source_text, lexer);
299		let output = parser.parse_entirely::<Vec<'_, ComponentValue>>().output.unwrap();
300
301		let mut overlays = CursorOverlaySet::new(&bump);
302		overlays.push_segment(OverlaySegment::new(
303			Span::new(SourceOffset(0), SourceOffset(0)),
304			snippet_cursors(&bump, "A"),
305			OverlayKind::InsertBefore,
306		));
307		overlays.push_segment(OverlaySegment::new(
308			Span::new(SourceOffset(0), SourceOffset(0)),
309			snippet_cursors(&bump, "B"),
310			OverlayKind::InsertBefore,
311		));
312		overlays.push_segment(OverlaySegment::new(
313			Span::new(SourceOffset(1), SourceOffset(1)),
314			snippet_cursors(&bump, "C"),
315			OverlayKind::InsertAfter,
316		));
317		overlays.push_segment(OverlaySegment::new(
318			Span::new(SourceOffset(1), SourceOffset(1)),
319			snippet_cursors(&bump, "D"),
320			OverlayKind::InsertAfter,
321		));
322
323		let mut str = String::new();
324		let mut stream = CursorOverlaySink::new(source_text, &overlays, CursorWriteSink::new(source_text, &mut str));
325		output.to_cursors(&mut stream);
326		assert_eq!(str, "A B x C D");
327	}
328}