use cosmic_text::AttrsOwned;
use lazy_static::lazy_static;
use regex::Regex;
use crate::font::language::Language;
use crate::{
font::cosmic_font::CosmicFont,
span::{position::Position, style::Style, Span},
};
lazy_static! {
static ref RE_ENDS_WITH_COMMAND: Regex = Regex::new(r#"\\(\w+)$"#).unwrap();
static ref RE_PUNCTUATION: Regex = Regex::new(r#"^(!|;|:|,|\.)"#).unwrap();
static ref RE_QUOTES: Regex = Regex::new(r#"([“|"](.*?)[”|"])"#).unwrap();
static ref RE_SPECIAL_CHARS: Regex = Regex::new(r#"(#|\$|%|&|_)"#).unwrap();
}
pub struct SpanColumn {
pub span: Span,
pub start: usize,
pub cosmic_font: CosmicFont,
pub tex_font: String,
pub language: Language,
}
impl SpanColumn {
pub fn new(span: Span, cosmic_font: CosmicFont, tex_font: &str, language: Language) -> Self {
Self {
span,
start: 0,
cosmic_font,
tex_font: tex_font.to_string(),
language,
}
}
pub fn is_word_in_body(&self, index: usize) -> bool {
self.span.0[index].position == Position::Body
}
pub fn to_cosmic(&self, end: usize) -> Vec<(String, AttrsOwned)> {
let mut cosmic_spans = vec![];
let mut span = vec![];
let mut style = Style::default();
let mut attrs = self.cosmic_font.regular.clone();
for word in self.span.0[self.start..end]
.iter()
.filter(|w| w.position == Position::Body)
{
if style == word.style {
span.push(word.word.clone());
}
else {
cosmic_spans.push((span.join(" "), attrs));
span.clear();
attrs = word.style.attrs(&self.cosmic_font);
style = word.style;
}
}
if !span.is_empty() {
cosmic_spans.push((span.join(" "), attrs));
}
cosmic_spans
}
pub fn to_tex(&self, end: Option<usize>, marginalia: bool) -> String {
let end = match end {
Some(end) => end,
None => self.span.0.len(),
};
let mut text = self.tex_font.clone();
let mut style = Style::default();
let mut position = Position::default();
for word in self.span.0[self.start..end].iter() {
let mut prefixes = vec![];
let mut suffixes = vec![];
if style != word.style {
let (prefix, suffix) = style.get_command(&word.style);
if let Some(prefix) = prefix {
prefixes.push(prefix);
}
if let Some(suffix) = suffix {
suffixes.push(suffix);
}
style = word.style;
}
if marginalia {
if position != word.position {
let command = position.get_command(&word.position);
if let Some(prefix) = command.0 {
prefixes.push(prefix);
}
if let Some(suffix) = command.1 {
suffixes.push(suffix);
}
position = word.position;
}
}
else if word.position == Position::Margin {
continue;
}
suffixes.iter().for_each(|s| text.push_str(s));
if RE_ENDS_WITH_COMMAND.captures(&text).is_some()
|| RE_PUNCTUATION.captures(&word.word).is_none()
{
text.push(' ');
}
prefixes.iter().for_each(|p| text.push_str(p));
text.push_str(&word.word);
}
match style {
Style::Regular => (),
Style::Bold | Style::Italic => text.push('}'),
Style::BoldItalic => text.push_str("}}"),
}
if let Position::Margin = position {
text.push('}');
}
if self.language != Language::English {
text += "}";
}
Self::santitize_tex(&mut text);
text
}
pub fn done(&self) -> bool {
self.start >= self.span.0.len()
}
fn santitize_tex(tex: &mut String) {
*tex = RE_SPECIAL_CHARS
.replace_all(&RE_QUOTES.replace_all(tex, "``$2''"), "\\$1")
.replace("~", "$\\sim$")
.replace("<", "\\textless")
.replace(">", "\\textgreater");
}
}
#[cfg(test)]
mod tests {
use crate::font::language::Language;
use crate::{font::cosmic_font::CosmicFont, span::Span, table::span_column::SpanColumn};
#[test]
fn test_textit() {
let md = "*This is italic* and this is regular.";
let column = get_column(md);
let tex = column.to_tex(None, true);
assert_eq!(tex, "\\font \\textit{This is italic} and this is regular.")
}
#[test]
fn test_bold_italic() {
let md = "**bold** *italic* ***bold and italic*** **bold**";
let column = get_column(md);
let tex = column.to_tex(None, true);
assert_eq!(
tex,
"\\font \\textbf{bold} \\textit{italic \\textbf{bold and italic}} \\textbf{bold}"
)
}
#[test]
fn test_marginnote() {
let md = "A `footnote *here* and` *there*";
let column = get_column(md);
let tex = column.to_tex(None, true);
assert_eq!(
tex,
"\\font A \\marginnote{\\noindent\\justifying\\tiny footnote \\textit{here} and} \\textit{there}"
);
let tex = column.to_tex(None, false);
assert_eq!(tex, "\\font A \\textit{there}");
}
fn get_column(md: &str) -> SpanColumn {
SpanColumn::new(
Span::from_md(md).unwrap(),
CosmicFont::default_left(),
"\\font",
Language::English,
)
}
}