Skip to main content

css_parse/
cursor_overlay_sink.rs

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