css_parse/
cursor_overlay_sink.rs1use crate::{
2 Cursor, CursorSink, CursorToSourceCursorSink, ParserReturn, SourceCursor, SourceCursorSink, SourceOffset, Span,
3 ToCursors, ToSpan,
4};
5use bumpalo::{Bump, collections::Vec};
6use std::collections::BTreeMap;
7
8#[derive(Debug)]
9pub struct CursorOverlaySet<'a> {
10 bump: &'a Bump,
11 overlays: BTreeMap<SourceOffset, (SourceOffset, Vec<'a, SourceCursor<'a>>)>,
12}
13
14impl<'a> CursorOverlaySet<'a> {
15 pub fn new(bump: &'a Bump) -> Self {
16 Self { bump, overlays: BTreeMap::new() }
17 }
18
19 pub fn insert<T: ToCursors>(&mut self, span: Span, parser_return: ParserReturn<'a, T>) {
20 let start = span.start();
21 let end = span.end();
22 let mut cursors = Vec::new_in(self.bump);
23 let mut sink = CursorToSourceCursorSink::new(parser_return.source_text, &mut cursors);
24 parser_return.to_cursors(&mut sink);
25 self.overlays.insert(start, (end, cursors));
26 }
27
28 pub(crate) fn find(&self, end: SourceOffset) -> Option<&(SourceOffset, Vec<'a, SourceCursor<'a>>)> {
29 self.overlays.range(..=end).rev().find(|&(_, &(overlay_end, _))| end < overlay_end).map(|(_, ret)| ret)
30 }
31}
32
33#[derive(Debug)]
42pub struct CursorOverlaySink<'a, T: SourceCursorSink<'a>> {
43 source_text: &'a str,
44 overlays: &'a CursorOverlaySet<'a>,
45 sink: T,
46 processed_overlay_ranges: BTreeMap<SourceOffset, SourceOffset>,
47}
48
49impl<'a, T: SourceCursorSink<'a>> CursorOverlaySink<'a, T> {
50 pub fn new(source_text: &'a str, overlays: &'a CursorOverlaySet<'a>, sink: T) -> Self {
51 Self { source_text, overlays, sink, processed_overlay_ranges: BTreeMap::new() }
52 }
53}
54
55impl<'a, T: SourceCursorSink<'a>> SourceCursorSink<'a> for CursorOverlaySink<'a, T> {
56 fn append(&mut self, c: SourceCursor<'a>) {
57 let cursor_start = c.to_span().start();
58 let cursor_end = c.to_span().end();
59
60 if let Some((&range_start, &range_end)) = self.processed_overlay_ranges.range(..=cursor_start).next_back() {
63 if cursor_start >= range_start && cursor_end <= range_end {
64 return;
66 }
67 }
68
69 let mut pos = cursor_start;
70 while pos < cursor_end {
71 if let Some((overlay_end, overlay)) = self.overlays.find(pos) {
73 for c in overlay {
74 self.sink.append(*c);
75 }
76 self.processed_overlay_ranges.insert(pos, *overlay_end);
77 pos = *overlay_end;
78 } else {
79 self.sink.append(c);
80 pos = c.to_span().end();
81 }
82 }
83 }
84}
85
86impl<'a, T: SourceCursorSink<'a>> CursorSink for CursorOverlaySink<'a, T> {
87 fn append(&mut self, c: Cursor) {
88 SourceCursorSink::append(self, SourceCursor::from(c, c.str_slice(self.source_text)))
89 }
90}
91
92#[cfg(test)]
93mod test {
94 use super::*;
95 use crate::{ComponentValue, CursorPrettyWriteSink, CursorWriteSink, QuoteStyle, T, ToCursors, ToSpan, parse};
96 use bumpalo::{Bump, collections::Vec};
97
98 #[test]
99 fn test_basic() {
100 let source_text = "black white";
102 let bump = Bump::default();
103 let output = parse!(in bump &source_text as (T![Ident], T![Ident])).output.unwrap();
104
105 let overlay_text = "green";
107 let overlay = parse!(in bump &overlay_text as T![Ident]);
108 let mut overlays = CursorOverlaySet::new(&bump);
109 overlays.insert(output.1.to_span(), overlay);
110
111 let mut str = String::new();
113 let mut stream = CursorOverlaySink::new(source_text, &overlays, CursorWriteSink::new(source_text, &mut str));
114 output.to_cursors(&mut stream);
115
116 assert_eq!(str, "black green");
118 }
119
120 #[test]
121 fn test_with_pretty_writer() {
122 let source_text = "foo{use:other;}";
124 let bump = Bump::default();
125 let output = parse!(in bump &source_text as Vec<'_, ComponentValue>).output.unwrap();
126 let ComponentValue::SimpleBlock(ref block) = output[1] else { panic!("output[1] was not a block") };
127 dbg!(block.to_span(), block.values.to_span());
128
129 let overlay_text = "inner{foo: bar;}";
131 let overlay = parse!(in bump &overlay_text as Vec<'_, ComponentValue>);
132 let mut overlays = CursorOverlaySet::new(&bump);
133 overlays.insert(dbg!(block.values.to_span()), overlay);
134
135 let mut str = String::new();
137 let mut stream = CursorOverlaySink::new(
138 source_text,
139 &overlays,
140 CursorPrettyWriteSink::new(source_text, &mut str, None, QuoteStyle::Double),
141 );
142 output.to_cursors(&mut stream);
143
144 assert_eq!(
146 str,
147 r#"
148foo {
149 inner {
150 foo: bar;
151 }
152}
153 "#
154 .trim()
155 );
156 }
157}