sevenmark_html/render/
document.rs1use maud::{Markup, html};
4use sevenmark_utils::Utf16OffsetConverter;
5
6use super::{brace, element, markdown};
7use crate::classes;
8use crate::config::RenderConfig;
9use crate::context::RenderContext;
10use crate::section::{Section, SectionTree, build_section_tree};
11use sevenmark_parser::ast::Element;
12
13pub fn render_document(ast: &[Element], config: &RenderConfig) -> String {
19 let tree = build_section_tree(ast);
20 let mut ctx = RenderContext::new(config);
21 let content = render_section_tree(&tree, config, &mut ctx);
22
23 let markup = html! {
24 (content)
25 @if !ctx.footnotes.is_empty() {
26 (brace::footnote::render_list(&ctx))
27 }
28 };
29
30 markup.into_string()
31}
32
33pub fn render_document_with_spans(ast: &[Element], config: &RenderConfig, input: &str) -> String {
43 let tree = build_section_tree(ast);
44 let converter = Utf16OffsetConverter::new(input);
45 let mut ctx = RenderContext::with_converter(config, &converter);
46 let content = render_section_tree(&tree, config, &mut ctx);
47
48 let markup = html! {
49 (content)
50 @if !ctx.footnotes.is_empty() {
51 (brace::footnote::render_list(&ctx))
52 }
53 };
54
55 markup.into_string()
56}
57
58fn render_section_tree(
60 tree: &SectionTree<'_>,
61 config: &RenderConfig,
62 ctx: &mut RenderContext,
63) -> Markup {
64 html! {
65 @for el in &tree.preamble {
66 (element::render_element(el, ctx))
67 }
68 @for section in &tree.sections {
69 (render_section(section, config, ctx))
70 }
71 }
72}
73
74fn render_section(section: &Section<'_>, config: &RenderConfig, ctx: &mut RenderContext) -> Markup {
76 let header_markup = markdown::header::render_with_path(
77 section.header_level,
78 section.header_section_index,
79 section.header_children,
80 §ion.section_path,
81 config,
82 ctx,
83 );
84
85 let section_content = html! {
86 div class=(classes::SECTION_CONTENT) {
87 @for el in §ion.content {
88 (element::render_element(el, ctx))
89 }
90 @for child in §ion.children {
91 (render_section(child, config, ctx))
92 }
93 }
94 };
95
96 if section.header_is_folded {
97 let class = format!("{} {}", classes::SECTION, classes::SECTION_FOLDED);
98 html! {
99 details class=(class) data-section=(section.header_section_index) {
100 summary { (header_markup) }
101 (section_content)
102 }
103 }
104 } else {
105 html! {
106 details class=(classes::SECTION) data-section=(section.header_section_index) open {
107 summary { (header_markup) }
108 (section_content)
109 }
110 }
111 }
112}