use pulldown_cmark::html;
use std::ops::Range;
use crate::ParseError;
use crate::ParseOptions;
use crate::format_facts::FormatFacts;
use crate::gfm::apply_gfm_render_policy;
use crate::ir::{
BlockCheckpointFact, CodeBlock, Frontmatter, Heading, HtmlBlock, InlineCode, InlineHtml, Ir, LinkDef, ListGroup,
Suppression, TextSlice,
};
use crate::line_index::LineIndex;
use crate::parse;
use crate::render::{RenderOptions, RenderProfile, render_cmark_gfm_html};
use crate::source::ByteSpan;
use crate::source::{CanonicalSource, Source};
use mdwright_math::{MathError, MathRegion};
pub fn render_html(source: &str) -> Result<String, ParseError> {
render_html_with_options(source, ParseOptions::default())
}
pub fn render_html_with_options(source: &str, opts: ParseOptions) -> Result<String, ParseError> {
render_html_with_render_options(source, opts, RenderOptions::default())
}
pub fn render_html_with_render_options(
source: &str,
opts: ParseOptions,
render: RenderOptions,
) -> Result<String, ParseError> {
let src = Source::new(source);
let canonical = CanonicalSource::from_source(&src);
let events = parse::collect_events_with_offsets(canonical, parse::options(opts))?;
let events = apply_gfm_render_policy(canonical.as_str(), events, opts.extensions().gfm);
match render.profile() {
RenderProfile::Pulldown => {
let mut out = String::with_capacity(canonical.as_str().len());
html::push_html(&mut out, events.into_iter());
Ok(out)
}
RenderProfile::CmarkGfm => Ok(render_cmark_gfm_html(events)),
}
}
#[derive(Debug)]
pub struct Document {
source: Source,
ir: Ir,
parse_options: ParseOptions,
}
impl Document {
#[tracing::instrument(level = "info", name = "Document::parse", skip(source), fields(len = source.len()))]
pub fn parse(source: &str) -> Result<Self, ParseError> {
Self::parse_with_options(source, ParseOptions::default())
}
pub fn parse_with_options(source: &str, opts: ParseOptions) -> Result<Self, ParseError> {
let source = Source::new(source);
let ir = Ir::parse(&source, opts)?;
Ok(Self {
source,
ir,
parse_options: opts,
})
}
#[must_use]
pub fn parse_options(&self) -> ParseOptions {
self.parse_options
}
#[must_use]
pub fn source(&self) -> &str {
self.source.canonical()
}
#[must_use]
pub fn original_source(&self) -> &str {
self.source.original()
}
#[must_use]
pub fn canonical_to_original_range(&self, range: Range<usize>) -> Range<usize> {
let span = ByteSpan::from_range(range);
self.source.to_original(span).range()
}
#[must_use]
pub fn line_index(&self) -> &LineIndex {
self.ir.line_index()
}
pub(crate) fn format_facts(&self) -> &FormatFacts {
&self.ir.format_facts
}
#[must_use]
pub fn prose_chunks(&self) -> &[TextSlice] {
&self.ir.prose_chunks
}
#[must_use]
pub fn autolinks(&self) -> &[crate::AutolinkFact] {
&self.ir.autolinks
}
#[must_use]
pub fn inline_codes(&self) -> &[InlineCode] {
&self.ir.inline_codes
}
#[must_use]
pub fn math_regions(&self) -> &[MathRegion] {
&self.ir.math_regions
}
#[must_use]
pub fn math_errors(&self) -> &[MathError] {
&self.ir.math_errors
}
#[must_use]
pub fn code_blocks(&self) -> &[CodeBlock] {
&self.ir.code_blocks
}
#[must_use]
pub fn html_blocks(&self) -> &[HtmlBlock] {
&self.ir.html_blocks
}
#[must_use]
pub fn inline_html(&self) -> &[InlineHtml] {
&self.ir.inline_html
}
#[must_use]
pub fn headings(&self) -> &[Heading] {
&self.ir.headings
}
#[must_use]
pub fn list_groups(&self) -> &[ListGroup] {
&self.ir.list_groups
}
#[must_use]
pub fn list_tightness_view(&self) -> Vec<(&ListGroup, bool)> {
self.ir
.list_groups
.iter()
.filter_map(|g| {
self.ir
.list_tightness
.iter()
.find(|(start, _)| *start == g.raw_range.start)
.map(|(_, tight)| (g, *tight))
})
.collect()
}
#[must_use]
pub fn link_defs(&self) -> Vec<LinkDef<'_>> {
self.ir
.refs
.iter()
.map(|t| LinkDef {
label: t.label_raw.as_str(),
dest: t.dest.as_str(),
title: t.title.as_deref(),
raw_range: t.raw_range.clone(),
})
.collect()
}
#[must_use]
pub fn block_checkpoints(&self) -> &[BlockCheckpointFact] {
&self.ir.block_checkpoints
}
#[must_use]
pub fn link_like_ranges(&self) -> &[Range<usize>] {
&self.ir.link_like_ranges
}
#[must_use]
pub fn frontmatter(&self) -> Option<&Frontmatter> {
self.ir.frontmatter.as_ref()
}
#[must_use]
pub fn suppressions(&self) -> &[Suppression] {
&self.ir.suppressions
}
}