use std::borrow::Cow;
use std::ops::Range;
use super::env::EnvKind;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum AnyDelim {
Paren,
Bracket,
Dollar,
Dollar2,
}
impl AnyDelim {
pub const fn is_display(self) -> bool {
matches!(self, Self::Bracket | Self::Dollar2)
}
pub const fn open(self) -> &'static str {
match self {
Self::Paren => r"\(",
Self::Bracket => r"\[",
Self::Dollar => "$",
Self::Dollar2 => "$$",
}
}
pub const fn close(self) -> &'static str {
match self {
Self::Paren => r"\)",
Self::Bracket => r"\]",
Self::Dollar => "$",
Self::Dollar2 => "$$",
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum InlineDelim {
Paren,
Dollar,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum DisplayDelim {
Bracket,
Dollar2,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum MathSpan {
Inline { delim: InlineDelim, body: MathBody },
Display { delim: DisplayDelim, body: MathBody },
Environment { env: EnvKind, body: MathBody },
}
impl MathSpan {
pub fn body(&self) -> &MathBody {
match self {
Self::Inline { body, .. } | Self::Display { body, .. } | Self::Environment { body, .. } => body,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MathBody {
range: Range<usize>,
transparent: Box<[Range<usize>]>,
}
impl MathBody {
pub fn new(range: Range<usize>, transparent: Box<[Range<usize>]>) -> Self {
Self { range, transparent }
}
#[must_use]
pub fn source_range(&self) -> Range<usize> {
self.range.clone()
}
pub fn as_str<'src>(&self, source: &'src str) -> Cow<'src, str> {
if self.transparent.is_empty() {
return Cow::Borrowed(source.get(self.range.clone()).unwrap_or(""));
}
let mut out = String::with_capacity(self.range.end.saturating_sub(self.range.start));
let mut cursor = self.range.start;
for run in &self.transparent {
let run_start = run.start.max(self.range.start);
let run_end = run.end.min(self.range.end);
if run_start >= run_end {
continue;
}
if cursor < run_start
&& let Some(slice) = source.get(cursor..run_start)
{
out.push_str(slice);
}
cursor = run_end;
}
if cursor < self.range.end
&& let Some(slice) = source.get(cursor..self.range.end)
{
out.push_str(slice);
}
Cow::Owned(out)
}
pub fn clean_offset_to_source(&self, clean_off: usize) -> usize {
if self.transparent.is_empty() {
return self.range.start.saturating_add(clean_off);
}
let mut consumed = 0usize;
let mut cursor = self.range.start;
for run in &self.transparent {
let run_start = run.start.max(self.range.start);
let run_end = run.end.min(self.range.end);
if run_start >= run_end {
continue;
}
let slice_len = run_start.saturating_sub(cursor);
if clean_off < consumed.saturating_add(slice_len) {
return cursor.saturating_add(clean_off.saturating_sub(consumed));
}
consumed = consumed.saturating_add(slice_len);
cursor = run_end;
}
cursor.saturating_add(clean_off.saturating_sub(consumed))
}
}
#[allow(clippy::enum_variant_names)]
#[derive(Clone, Debug)]
pub enum MathError {
UnbalancedDelim {
delim: AnyDelim,
range: Range<usize>,
},
UnbalancedEnv {
name: String,
range: Range<usize>,
},
UnbalancedBraces {
offset: usize,
region: Range<usize>,
},
}