use crate::build_common::{make_ord, make_span, mathsym};
use crate::context::KatexContext;
use crate::dom_tree::HtmlDomNode;
use crate::mathml_tree::{MathDomNode, MathNode, MathNodeType, TextNode};
use crate::options::Options;
use crate::parser::parse_node::{NodeType, ParseNode};
use crate::types::{Mode, ParseError};
use phf::phf_map;
static CSS_SPACE: phf::Map<&'static str, &'static str> = phf_map! {
"\\nobreak" => "nobreak",
"\\allowbreak" => "allowbreak",
};
static REGULAR_SPACE: phf::Map<&'static str, Option<&'static str>> = phf_map! {
" " => None,
"\\ " => None,
"~" => Some("nobreak"),
"\\space" => None,
"\\nobreakspace" => Some("nobreak"),
};
fn html_builder(
node: &ParseNode,
options: &Options,
ctx: &KatexContext,
) -> Result<HtmlDomNode, ParseError> {
let ParseNode::Spacing(spacing_node) = node else {
return Err(ParseError::new("Expected spacing node"));
};
REGULAR_SPACE.get(&spacing_node.text).map_or_else(
|| {
CSS_SPACE.get(&spacing_node.text).map_or_else(
|| {
Err(ParseError::new(format!(
"Unknown type of space: {}",
spacing_node.text
)))
},
|class_name| {
let classes = vec!["mspace".to_owned(), (*class_name).to_owned()];
Ok(make_span(classes, vec![], Some(options), None).into())
},
)
},
|class_name_opt| {
let class_name = class_name_opt.unwrap_or("").to_owned();
let mspace_classes = vec!["mspace".to_owned(), class_name.clone()];
if spacing_node.mode == Mode::Text {
let mut ord = make_ord(ctx, &ParseNode::Spacing(spacing_node.clone()), options)?;
if let Some(classes) = ord.classes_mut() {
classes.push(class_name);
} else {
return Err(ParseError::new("Generated ord node should have classes"));
}
Ok(ord)
} else {
let symbol = mathsym(ctx, &spacing_node.text, Mode::Math, options, None)?;
Ok(make_span(mspace_classes, vec![symbol.into()], Some(options), None).into())
}
},
)
}
fn mathml_builder(
node: &ParseNode,
_options: &Options,
_ctx: &KatexContext,
) -> Result<MathDomNode, ParseError> {
let ParseNode::Spacing(spacing_node) = node else {
return Err(ParseError::new("Expected spacing node"));
};
if REGULAR_SPACE.contains_key(&spacing_node.text) {
Ok(MathNode::builder()
.node_type(MathNodeType::Mtext)
.children(vec![MathDomNode::Text(TextNode {
text: "\u{00a0}".to_owned(), })])
.build()
.into())
} else if CSS_SPACE.contains_key(&spacing_node.text) {
Ok(MathNode::builder()
.node_type(MathNodeType::Mspace)
.build()
.into())
} else {
Err(ParseError::new(format!(
"Unknown type of space: {}",
spacing_node.text
)))
}
}
pub fn define_spacing(ctx: &mut KatexContext) {
ctx.define_function_builders(NodeType::Spacing, Some(html_builder), Some(mathml_builder));
}