use crate::{
Cursor, CursorSink, CursorToSourceCursorSink, Kind, ParserReturn, SourceCursor, SourceCursorSink, SourceOffset,
Span, ToCursors, ToSpan,
};
use bumpalo::{Bump, collections::Vec};
use std::collections::BTreeMap;
#[derive(Debug)]
pub struct CursorOverlaySet<'a> {
bump: &'a Bump,
overlays: BTreeMap<SourceOffset, (SourceOffset, Vec<'a, SourceCursor<'a>>)>,
}
impl<'a> CursorOverlaySet<'a> {
pub fn new(bump: &'a Bump) -> Self {
Self { bump, overlays: BTreeMap::new() }
}
pub fn insert<T: ToCursors>(&mut self, span: Span, parser_return: ParserReturn<'a, T>) {
let start = span.start();
let end = span.end();
let mut cursors = Vec::new_in(self.bump);
let mut sink = CursorToSourceCursorSink::new(parser_return.source_text, &mut cursors);
parser_return.to_cursors(&mut sink);
self.overlays.insert(start, (end, cursors));
}
pub(crate) fn find(&self, end: SourceOffset) -> Option<&(SourceOffset, Vec<'a, SourceCursor<'a>>)> {
self.overlays.range(..=end).rev().find(|&(_, &(overlay_end, _))| end < overlay_end).map(|(_, ret)| ret)
}
}
#[derive(Debug)]
pub struct CursorOverlaySink<'a, T: SourceCursorSink<'a>> {
source_text: &'a str,
overlays: &'a CursorOverlaySet<'a>,
sink: T,
processed_overlay_ranges: BTreeMap<SourceOffset, SourceOffset>,
#[cfg(debug_assertions)]
seen_eof: bool,
}
impl<'a, T: SourceCursorSink<'a>> CursorOverlaySink<'a, T> {
pub fn new(source_text: &'a str, overlays: &'a CursorOverlaySet<'a>, sink: T) -> Self {
Self {
source_text,
overlays,
sink,
processed_overlay_ranges: BTreeMap::new(),
#[cfg(debug_assertions)]
seen_eof: false,
}
}
}
impl<'a, T: SourceCursorSink<'a>> SourceCursorSink<'a> for CursorOverlaySink<'a, T> {
fn append(&mut self, c: SourceCursor<'a>) {
let cursor_start = c.to_span().start();
let cursor_end = c.to_span().end();
if let Some((&range_start, &range_end)) = self.processed_overlay_ranges.range(..=cursor_start).next_back()
&& cursor_start >= range_start
&& cursor_end <= range_end
{
return;
}
let mut pos = cursor_start;
while pos < cursor_end {
if let Some((overlay_end, overlay)) = self.overlays.find(pos) {
for c in overlay {
if *c != Kind::Eof {
self.sink.append(*c);
}
}
self.processed_overlay_ranges.insert(pos, *overlay_end);
pos = *overlay_end;
} else {
self.sink.append(c);
pos = c.to_span().end();
}
}
}
}
impl<'a, T: SourceCursorSink<'a>> CursorSink for CursorOverlaySink<'a, T> {
fn append(&mut self, c: Cursor) {
#[cfg(debug_assertions)]
{
debug_assert!(!self.seen_eof, "Received cursor after EOF: {:?}", c);
if c == Kind::Eof {
self.seen_eof = true;
}
}
SourceCursorSink::append(self, SourceCursor::from(c, c.str_slice(self.source_text)))
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::{
ComponentValue, CursorPrettyWriteSink, CursorWriteSink, EmptyAtomSet, Parser, QuoteStyle, T, ToCursors, ToSpan,
};
use bumpalo::{Bump, collections::Vec};
use css_lexer::Lexer;
#[test]
fn test_basic() {
let source_text = "black white";
let bump = Bump::default();
let lexer = Lexer::new(&EmptyAtomSet::ATOMS, source_text);
let mut p = Parser::new(&bump, source_text, lexer);
let output = p.parse_entirely::<(T![Ident], T![Ident])>().output.unwrap();
let overlay_text = "green";
let lexer = Lexer::new(&EmptyAtomSet::ATOMS, overlay_text);
let mut p = Parser::new(&bump, overlay_text, lexer);
let overlay = p.parse_entirely::<T![Ident]>();
let mut overlays = CursorOverlaySet::new(&bump);
overlays.insert(output.1.to_span(), overlay);
let mut str = String::new();
let mut stream = CursorOverlaySink::new(source_text, &overlays, CursorWriteSink::new(source_text, &mut str));
output.to_cursors(&mut stream);
assert_eq!(str, "black green");
}
#[test]
fn test_with_pretty_writer() {
let source_text = "foo{use:other;}";
let bump = Bump::default();
let lexer = Lexer::new(&EmptyAtomSet::ATOMS, source_text);
let mut p = Parser::new(&bump, source_text, lexer);
let output = p.parse_entirely::<Vec<'_, ComponentValue>>().output.unwrap();
let ComponentValue::SimpleBlock(ref block) = output[1] else { panic!("output[1] was not a block") };
dbg!(block.to_span(), block.values.to_span());
let overlay_text = "inner{foo: bar;}";
let lexer = Lexer::new(&EmptyAtomSet::ATOMS, overlay_text);
let mut p = Parser::new(&bump, overlay_text, lexer);
let overlay = p.parse_entirely::<Vec<'_, ComponentValue>>();
let mut overlays = CursorOverlaySet::new(&bump);
overlays.insert(dbg!(block.values.to_span()), overlay);
let mut str = String::new();
let mut stream = CursorOverlaySink::new(
source_text,
&overlays,
CursorPrettyWriteSink::new(source_text, &mut str, None, QuoteStyle::Double),
);
output.to_cursors(&mut stream);
assert_eq!(
str,
r#"
foo {
inner {
foo: bar;
}
}
"#
.trim()
);
}
}