use alloc::vec::Vec;
use vize_l0::{Span, String};
use super::source_map::SourceMapBuilder;
mod escape;
mod expression_links;
pub use expression_links::expression_links;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SpanLink {
pub generated: Span,
pub authored: Span,
pub name: Option<String>,
pub segment: bool,
}
impl SpanLink {
#[inline]
fn rebased(&self, base: u32) -> Self {
Self {
generated: Span::new(self.generated.start + base, self.generated.end + base),
..self.clone()
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EmitDocument {
text: String,
links: Vec<SpanLink>,
recording: bool,
}
impl Default for EmitDocument {
#[inline]
fn default() -> Self {
Self::new(true)
}
}
impl From<String> for EmitDocument {
#[inline]
fn from(text: String) -> Self {
Self {
text,
links: Vec::new(),
recording: true,
}
}
}
impl EmitDocument {
#[inline]
pub fn new(recording: bool) -> Self {
Self::with_capacity(0, recording)
}
#[inline]
pub fn with_capacity(capacity: usize, recording: bool) -> Self {
Self {
text: String::with_capacity(capacity),
links: Vec::new(),
recording,
}
}
#[inline]
pub fn unrecorded(text: String) -> Self {
Self {
text,
links: Vec::new(),
recording: false,
}
}
#[inline]
pub fn plain(text: &str) -> Self {
Self::from(String::new(text))
}
#[inline]
pub fn mapped(text: &str, source: u32) -> Self {
let mut fragment = Self::default();
fragment.push_mapped(text, source);
fragment
}
#[inline]
pub fn is_recording(&self) -> bool {
self.recording
}
#[inline]
pub fn len(&self) -> usize {
self.text.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.text.is_empty()
}
#[inline]
pub fn as_str(&self) -> &str {
&self.text
}
#[inline]
pub fn links(&self) -> &[SpanLink] {
&self.links
}
#[inline]
pub fn into_string(self) -> String {
self.text
}
#[inline]
pub fn into_parts(self) -> (String, Vec<SpanLink>) {
(self.text, self.links)
}
#[inline]
pub fn from_parts(text: String, links: Vec<SpanLink>) -> Self {
Self {
text,
links,
recording: true,
}
}
#[inline]
pub fn push_str(&mut self, text: &str) {
self.text.push_str(text);
}
#[inline]
pub fn insert_str(&mut self, at: usize, text: &str) {
self.text.insert_str(at, text);
let (at, grown) = (at as u32, text.len() as u32);
for link in &mut self.links {
let starts_after = link.generated.start >= at;
if starts_after {
link.generated.start += grown;
}
if link.generated.end > at || (starts_after && link.generated.end == at) {
link.generated.end += grown;
}
}
}
#[inline]
pub fn push_char(&mut self, ch: char) {
self.text.push(ch);
}
#[inline]
fn cursor(&self) -> u32 {
self.text.len() as u32
}
#[inline(always)]
fn record(&mut self, generated: Span, authored: Span, name: Option<&str>) {
if self.recording {
self.links.push(SpanLink {
generated,
authored,
name: name.map(String::new),
segment: true,
});
}
}
#[inline(always)]
fn push_recorded(&mut self, text: &str, authored: Span, name: Option<&str>) {
if self.recording {
let start = self.cursor();
self.links.push(SpanLink {
generated: Span::new(start, start + text.len() as u32),
authored,
name: name.map(String::new),
segment: true,
});
}
self.text.push_str(text);
}
#[inline]
pub fn anchor(&mut self, source: u32) {
let at = self.cursor();
self.record(Span::new(at, at), Span::new(source, source), None);
}
#[inline]
pub fn push_mapped(&mut self, text: &str, source: u32) {
self.push_recorded(text, Span::new(source, source), None);
}
#[inline]
pub fn push_linked(&mut self, text: &str, authored: Span) {
self.push_recorded(text, authored, None);
}
#[inline]
pub fn push_named(&mut self, text: &str, authored: Span, name: &str) {
self.push_recorded(text, authored, Some(name));
}
#[inline(always)]
pub fn push_expression(&mut self, code: &str, span: Span, source_text: &str) {
if self.recording {
let base = self.cursor();
self.links.extend(
expression_links(code, span, source_text)
.iter()
.map(|link| link.rebased(base)),
);
}
self.text.push_str(code);
}
#[inline(always)]
pub fn push_spanned(&mut self, other: &EmitDocument) {
if self.recording {
let base = self.cursor();
self.links
.extend(other.links.iter().map(|link| link.rebased(base)));
}
self.text.push_str(&other.text);
}
pub fn write_segments(&self, builder: &mut SourceMapBuilder) {
for link in self.links.iter().filter(|link| link.segment) {
let (generated, source) = (link.generated.start as usize, link.authored.start);
match &link.name {
Some(name) => builder.add_named(generated, source, name),
None => builder.add_raw(generated, source),
}
}
}
pub fn source_map(&self, filename: &str, source: &str) -> String {
let mut builder = SourceMapBuilder::new();
self.write_segments(&mut builder);
builder.finish(&self.text, filename, source)
}
}
#[cfg(test)]
mod tests;