ftml_viewer_components/components/omdoc/
narration.rs

1use crate::{components::omdoc::OMDocT, FTMLString, FTMLStringMath};
2use flams_ontology::{
3    content::{declarations::symbols::ArgSpec, terms::Term},
4    narration::{
5        paragraphs::{ParagraphFormatting, ParagraphKind},
6        problems::CognitiveDimension,
7    },
8    uris::{DocumentElementURI, DocumentURI, ModuleURI, NarrativeURI, SymbolURI, URI},
9};
10use flams_utils::vecmap::{VecMap, VecSet};
11
12use super::{
13    content::{OMDocExtension, OMDocModule, OMDocMorphism, OMDocStructure, OMDocSymbol},
14    OMDoc,
15};
16use flams_web_utils::components::{Block, Header, HeaderLeft, HeaderRight, LazyCollapsible};
17use leptos::{either::Either, prelude::*};
18use thaw::{Text, TextTag};
19
20#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
21#[cfg_attr(feature = "ts", derive(tsify_next::Tsify))]
22#[cfg_attr(feature = "ts", tsify(into_wasm_abi, from_wasm_abi))]
23pub struct OMDocDocument {
24    pub uri: DocumentURI,
25    pub title: Option<String>,
26    #[cfg_attr(feature = "ts", tsify(type = "ModuleURI[]"))]
27    pub uses: VecSet<ModuleURI>,
28    pub children: Vec<OMDocDocumentElement>,
29}
30impl super::OMDocT for OMDocDocument {
31    fn into_view(self) -> impl IntoView {
32        view! {<Block show_separator=false>
33          <HeaderLeft slot>{super::uses("Uses",self.uses.0)}</HeaderLeft>
34          {self.children.into_iter().map(super::OMDocT::into_view).collect_view()}
35        </Block>}
36    }
37}
38impl From<OMDocDocument> for OMDoc {
39    #[inline]
40    fn from(value: OMDocDocument) -> Self {
41        Self::Document(value)
42    }
43}
44
45#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
46#[cfg_attr(feature = "ts", derive(tsify_next::Tsify))]
47#[cfg_attr(feature = "ts", tsify(into_wasm_abi, from_wasm_abi))]
48pub struct OMDocSection {
49    pub title: Option<String>,
50    pub uri: DocumentElementURI,
51    #[cfg_attr(feature = "ts", tsify(type = "ModuleURI[]"))]
52    pub uses: VecSet<ModuleURI>,
53    pub children: Vec<OMDocDocumentElement>,
54}
55impl super::OMDocT for OMDocSection {
56    fn into_view(self) -> impl IntoView {
57        if let Some(title) = self.title {
58            Either::Left(view! {
59              <Block>
60                <Header slot><b style="font-size:larger"><FTMLString html=title/></b></Header>
61                <HeaderLeft slot>{super::uses("Uses",self.uses.0)}</HeaderLeft>
62                {self.children.into_iter().map(super::OMDocT::into_view).collect_view()}
63              </Block>
64            })
65        } else {
66            Either::Right(
67                self.children
68                    .into_iter()
69                    .map(super::OMDocT::into_view)
70                    .collect_view(),
71            )
72        }
73    }
74}
75impl From<OMDocSection> for OMDoc {
76    #[inline]
77    fn from(value: OMDocSection) -> Self {
78        Self::Section(value)
79    }
80}
81impl From<OMDocSection> for OMDocDocumentElement {
82    #[inline]
83    fn from(value: OMDocSection) -> Self {
84        Self::Section(value)
85    }
86}
87
88#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
89#[cfg_attr(feature = "ts", derive(tsify_next::Tsify))]
90#[cfg_attr(feature = "ts", tsify(into_wasm_abi, from_wasm_abi))]
91pub struct OMDocSlide {
92    pub uri: DocumentElementURI,
93    #[cfg_attr(feature = "ts", tsify(type = "ModuleURI[]"))]
94    pub uses: VecSet<ModuleURI>,
95    pub children: Vec<OMDocDocumentElement>,
96}
97impl super::OMDocT for OMDocSlide {
98    fn into_view(self) -> impl IntoView {
99        view! {
100          <Block>
101            <Header slot><b style="font-size:larger">"Slide"</b></Header>
102            <HeaderLeft slot>{super::uses("Uses",self.uses.0)}</HeaderLeft>
103            {self.children.into_iter().map(super::OMDocT::into_view).collect_view()}
104          </Block>
105        }
106    }
107}
108
109impl From<OMDocSlide> for OMDoc {
110    #[inline]
111    fn from(value: OMDocSlide) -> Self {
112        Self::Slide(value)
113    }
114}
115impl From<OMDocSlide> for OMDocDocumentElement {
116    #[inline]
117    fn from(value: OMDocSlide) -> Self {
118        Self::Slide(value)
119    }
120}
121
122#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
123#[cfg_attr(feature = "ts", derive(tsify_next::Tsify))]
124#[cfg_attr(feature = "ts", tsify(into_wasm_abi, from_wasm_abi))]
125pub struct OMDocVariable {
126    pub uri: DocumentElementURI,
127    pub arity: ArgSpec,
128    pub macro_name: Option<String>,
129    pub tp: Option<Term>, //Option<String>,
130    pub df: Option<Term>, //Option<String>,
131    pub is_seq: bool,
132}
133impl super::OMDocT for OMDocVariable {
134    fn into_view(self) -> impl IntoView {
135        let OMDocVariable {
136            uri,
137            df,
138            tp,
139            arity,
140            is_seq,
141            macro_name,
142        } = self;
143        //let show_separator = !notations.is_empty();
144        let varstr = if is_seq {
145            "Sequence Variable "
146        } else {
147            "Variable "
148        };
149        let name = uri.name().last_name().to_string();
150        view! {
151            <Block show_separator=false>
152                <Header slot><span>
153                    <b>{varstr}<span class="ftml-var-comp">{name}</span></b>
154                    {macro_name.map(|name| view!(<span>" ("<Text tag=TextTag::Code>"\\"{name}</Text>")"</span>))}
155                    {tp.map(|t| view! {
156                      " of type "{
157                        crate::remote::get!(present(t.clone()) = html => {
158                          view!(<FTMLStringMath html/>)
159                        })
160                      }
161                  })}
162                </span></Header>
163                <HeaderLeft slot>
164                  {super::content::do_notations(URI::Narrative(uri.into()),arity)}
165                </HeaderLeft>
166                <HeaderRight slot><span style="white-space:nowrap;">{df.map(|t| view! {
167                    "Definiens: "{
168                      crate::remote::get!(present(t.clone()) = html => {
169                        view!(<FTMLStringMath html/>)
170                      })
171                    }
172                })}</span></HeaderRight>
173                <span/>
174            </Block>
175        }
176    }
177}
178impl From<OMDocVariable> for OMDoc {
179    #[inline]
180    fn from(value: OMDocVariable) -> Self {
181        Self::Variable(value)
182    }
183}
184impl From<OMDocVariable> for OMDocDocumentElement {
185    #[inline]
186    fn from(value: OMDocVariable) -> Self {
187        Self::Variable(value)
188    }
189}
190
191#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
192#[cfg_attr(feature = "ts", derive(tsify_next::Tsify))]
193#[cfg_attr(feature = "ts", tsify(into_wasm_abi, from_wasm_abi))]
194pub struct OMDocParagraph {
195    pub uri: DocumentElementURI,
196    pub kind: ParagraphKind,
197    pub formatting: ParagraphFormatting,
198    #[cfg_attr(feature = "ts", tsify(type = "ModuleURI[]"))]
199    pub uses: VecSet<ModuleURI>,
200    #[cfg_attr(feature = "ts", tsify(type = "ModuleURI[]"))]
201    pub fors: VecMap<SymbolURI, Option<Term>>, //Option<String>>,
202    pub title: Option<String>,
203    pub children: Vec<OMDocDocumentElement>,
204    pub definition_like: bool,
205}
206impl super::OMDocT for OMDocParagraph {
207    fn into_view(self) -> impl IntoView {
208        let Self {
209            uri,
210            kind,
211            uses,
212            fors,
213            title,
214            children,
215            definition_like,
216            ..
217        } = self;
218        let title = title.unwrap_or_else(|| uri.name().last_name().to_string());
219        view! {
220          <Block>
221            <Header slot><b>
222              {super::doc_elem_name(uri,Some(kind.as_display_str()),title)}
223            </b></Header>
224            <HeaderLeft slot>{super::uses("Uses",uses.0)}</HeaderLeft>
225            <HeaderRight slot>{super::comma_sep(
226              if definition_like {"Defines"} else {"Concerns"},
227              fors.into_iter().map(|(k,t)| view!{
228                {super::symbol_name(&k,k.name().last_name().as_ref())}
229                {t.map(|t| view!{" as "{
230                  crate::remote::get!(present(t.clone()) = html => {
231                    view!(<FTMLStringMath html/>)
232                  })
233                }})}
234              })
235            )}</HeaderRight>
236            {children.into_iter().map(super::OMDocT::into_view).collect_view()}
237          </Block>
238        }
239    }
240}
241impl From<OMDocParagraph> for OMDoc {
242    #[inline]
243    fn from(value: OMDocParagraph) -> Self {
244        Self::Paragraph(value)
245    }
246}
247impl From<OMDocParagraph> for OMDocDocumentElement {
248    #[inline]
249    fn from(value: OMDocParagraph) -> Self {
250        Self::Paragraph(value)
251    }
252}
253
254#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
255#[cfg_attr(feature = "ts", derive(tsify_next::Tsify))]
256#[cfg_attr(feature = "ts", tsify(into_wasm_abi, from_wasm_abi))]
257pub struct OMDocProblem {
258    pub uri: DocumentElementURI,
259    pub sub_problem: bool,
260    pub autogradable: bool,
261    pub points: Option<f32>,
262    pub title: Option<String>,
263    pub preconditions: Vec<(CognitiveDimension, SymbolURI)>,
264    pub objectives: Vec<(CognitiveDimension, SymbolURI)>,
265    #[cfg_attr(feature = "ts", tsify(type = "ModuleURI[]"))]
266    pub uses: VecSet<ModuleURI>,
267    pub children: Vec<OMDocDocumentElement>,
268}
269impl super::OMDocT for OMDocProblem {
270    fn into_view(self) -> impl IntoView {
271        let Self {
272            uri,
273            title,
274            uses,
275            objectives,
276            children,
277            ..
278        } = self;
279        let title = title.unwrap_or_else(|| uri.name().last_name().to_string());
280        view! {
281          <Block>
282            <Header slot><b>
283              {super::doc_elem_name(uri,Some("Problem"),title)}
284            </b></Header>
285            <HeaderLeft slot>{super::uses("Uses",uses.0)}</HeaderLeft>
286            <HeaderRight slot>{super::comma_sep(
287              "Objectives",
288              objectives.into_iter().map(|(dim,sym)| view!{
289                {super::symbol_name(&sym,sym.name().last_name().as_ref())}
290                " ("{dim.to_string()}")"
291              })
292            )}</HeaderRight>
293            {children.into_iter().map(super::OMDocT::into_view).collect_view()}
294          </Block>
295        }
296    }
297}
298impl From<OMDocProblem> for OMDoc {
299    #[inline]
300    fn from(value: OMDocProblem) -> Self {
301        Self::Problem(value)
302    }
303}
304impl From<OMDocProblem> for OMDocDocumentElement {
305    #[inline]
306    fn from(value: OMDocProblem) -> Self {
307        Self::Problem(value)
308    }
309}
310
311#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
312#[serde(tag = "type")]
313#[cfg_attr(feature = "ts", derive(tsify_next::Tsify))]
314#[cfg_attr(feature = "ts", tsify(into_wasm_abi, from_wasm_abi))]
315pub enum OMDocDocumentElement {
316    Slide(OMDocSlide),
317    Section(OMDocSection),
318    Module(OMDocModule<OMDocDocumentElement>),
319    Morphism(OMDocMorphism<OMDocDocumentElement>),
320    Structure(OMDocStructure<OMDocDocumentElement>),
321    Extension(OMDocExtension<OMDocDocumentElement>),
322    DocumentReference {
323        uri: DocumentURI,
324        title: Option<String>,
325    },
326    Variable(OMDocVariable),
327    Paragraph(OMDocParagraph),
328    Problem(OMDocProblem),
329    TopTerm {
330        uri: DocumentElementURI,
331        term: Term,
332    },
333    SymbolDeclaration(
334        #[cfg_attr(feature = "ts", tsify(type = "SymbolURI|OMDocSymbol"))]
335        either::Either<SymbolURI, OMDocSymbol>,
336    ),
337}
338impl super::sealed::Sealed for OMDocDocumentElement {}
339impl super::OMDocDecl for OMDocDocumentElement {}
340impl super::OMDocT for OMDocDocumentElement {
341    fn into_view(self) -> impl IntoView {
342        match self {
343            Self::Slide(s) => s.into_view().into_any(),
344            Self::Section(s) => s.into_view().into_any(),
345            Self::Module(m) => m.into_view().into_any(),
346            Self::Morphism(m) => m.into_view().into_any(),
347            Self::Structure(s) => s.into_view().into_any(),
348            Self::Extension(e) => e.into_view().into_any(),
349            Self::DocumentReference { uri, title } => doc_ref(uri, title).into_any(),
350            Self::Variable(v) => v.into_view().into_any(),
351            Self::Paragraph(p) => p.into_view().into_any(),
352            Self::Problem(e) => e.into_view().into_any(),
353            Self::TopTerm { term, .. } => view! {
354              <Block show_separator=false>
355                <Header slot><span><b>"Term "</b>{
356                  crate::remote::get!(present(term.clone()) = html => {
357                    view!(<FTMLStringMath html/>)
358                  })
359                }</span></Header>
360                ""
361              </Block>
362            }
363            .into_any(),
364            Self::SymbolDeclaration(either::Right(s)) => s.into_view().into_any(),
365            Self::SymbolDeclaration(either::Left(u)) => {
366                view! {<div style="color:red;">"Symbol "{u.to_string()}" not found"</div>}
367                    .into_any()
368            }
369        }
370    }
371}
372
373pub(crate) fn doc_ref(uri: DocumentURI, title: Option<String>) -> impl IntoView {
374    let name = title.unwrap_or_else(|| uri.name().last_name().to_string());
375    let uricl = uri.clone();
376    view! {//<Block>
377    <LazyCollapsible>
378      <Header slot><b>"Document "{super::doc_name(&uri, name)}</b></Header>
379      <div style="padding-left:15px;">{
380        let uri = uricl.clone();
381        let r = Resource::new(|| (),move |()| crate::remote::server_config.omdoc(NarrativeURI::Document(uri.clone()).into()));
382        view!{
383          <Suspense fallback=|| view!(<flams_web_utils::components::Spinner/>)>{move || {
384            if let Some(Ok((_,omdoc))) = r.get() {
385              let OMDoc::Document(omdoc) = omdoc else {unreachable!()};
386              Some(omdoc.into_view())
387            } else {None}
388          }}</Suspense>
389        }
390      }</div>
391    </LazyCollapsible>
392    } //</Block>}
393}
394
395impl From<OMDocDocumentElement> for OMDoc {
396    fn from(value: OMDocDocumentElement) -> Self {
397        match value {
398            OMDocDocumentElement::Slide(s) => Self::Slide(s),
399            OMDocDocumentElement::Section(s) => Self::Section(s),
400            OMDocDocumentElement::Module(m) => Self::DocModule(m),
401            OMDocDocumentElement::Morphism(m) => Self::DocMorphism(m),
402            OMDocDocumentElement::Structure(s) => Self::DocStructure(s),
403            OMDocDocumentElement::Extension(e) => Self::DocExtension(e),
404            OMDocDocumentElement::DocumentReference { uri, title } => {
405                Self::DocReference { uri, title }
406            }
407            OMDocDocumentElement::SymbolDeclaration(either::Right(s)) => Self::SymbolDeclaration(s),
408            OMDocDocumentElement::Variable(v) => Self::Variable(v),
409            OMDocDocumentElement::Paragraph(p) => Self::Paragraph(p),
410            OMDocDocumentElement::Problem(e) => Self::Problem(e),
411            OMDocDocumentElement::TopTerm { uri, term } => Self::Term { uri, term },
412            OMDocDocumentElement::SymbolDeclaration(either::Left(u)) => Self::Other(u.to_string()),
413        }
414    }
415}
416
417#[cfg(feature = "ssr")]
418mod froms {
419    use super::{
420        OMDocDocument, OMDocDocumentElement, OMDocParagraph, OMDocProblem, OMDocSection,
421        OMDocSlide, OMDocVariable,
422    };
423    use crate::components::omdoc::content::{
424        OMDocExtension, OMDocModule, OMDocMorphism, OMDocStructure, OMDocSymbol,
425    };
426    use flams_ontology::{
427        content::{declarations::OpenDeclaration, ModuleLike},
428        narration::{
429            documents::Document, paragraphs::LogicalParagraph, problems::Problem,
430            sections::Section, variables::Variable, DocumentElement, NarrationTrait,
431        },
432        uris::{DocumentElementURI, ModuleURI},
433        Checked,
434    };
435    use flams_system::backend::Backend;
436    use flams_utils::{vecmap::VecSet, CSS};
437
438    impl OMDocSection {
439        pub fn from_section<B: Backend>(
440            Section {
441                title,
442                children,
443                uri,
444                ..
445            }: &Section<Checked>,
446            backend: &B, //&mut StringPresenter<'_,B>,
447            css: &mut VecSet<CSS>,
448        ) -> Self {
449            let mut uses = VecSet::new();
450            let mut imports = VecSet::new();
451            let title = title.and_then(|r| {
452                if let Some((c, s)) = backend.get_html_fragment(uri.document(), r) {
453                    if s.trim().is_empty() {
454                        None
455                    } else {
456                        for c in c {
457                            css.insert(c)
458                        }
459                        Some(s)
460                    }
461                } else {
462                    None
463                }
464            });
465            let children =
466                OMDocDocumentElement::do_children(backend, children, &mut uses, &mut imports, css);
467            Self {
468                title,
469                uri: uri.clone(),
470                uses,
471                children,
472            }
473        }
474    }
475
476    impl OMDocSlide {
477        pub fn from_slide<B: Backend>(
478            children: &[DocumentElement<Checked>],
479            uri: &DocumentElementURI,
480            backend: &B, //&mut StringPresenter<'_,B>,
481            css: &mut VecSet<CSS>,
482        ) -> Self {
483            let mut uses = VecSet::new();
484            let mut imports = VecSet::new();
485            let children =
486                OMDocDocumentElement::do_children(backend, children, &mut uses, &mut imports, css);
487            Self {
488                uri: uri.clone(),
489                uses,
490                children,
491            }
492        }
493    }
494
495    impl OMDocParagraph {
496        pub fn from_paragraph<B: Backend>(
497            LogicalParagraph {
498                uri,
499                kind,
500                formatting,
501                fors,
502                title,
503                children,
504                styles,
505                ..
506            }: &LogicalParagraph<Checked>,
507            backend: &B, //&mut StringPresenter<'_,B>,
508            css: &mut VecSet<CSS>,
509        ) -> Self {
510            let definition_like = kind.is_definition_like(styles);
511            let mut uses = VecSet::new();
512            let mut imports = VecSet::new();
513            let title = title.and_then(|r| {
514                if let Some((c, s)) = backend.get_html_fragment(uri.document(), r) {
515                    if s.trim().is_empty() {
516                        None
517                    } else {
518                        for c in c {
519                            css.insert(c)
520                        }
521                        Some(s)
522                    }
523                } else {
524                    None
525                }
526            });
527            let children =
528                OMDocDocumentElement::do_children(backend, children, &mut uses, &mut imports, css);
529            Self {
530                kind: *kind,
531                formatting: *formatting,
532                fors: fors.clone(), //.0.iter().map(|(k,v)| (k.clone(),v.as_ref().and_then(|t| backend.present(t).ok()))).collect(),
533                title,
534                uri: uri.clone(),
535                uses,
536                children,
537                definition_like,
538            }
539        }
540    }
541
542    impl OMDocProblem {
543        #[allow(clippy::cast_possible_truncation)]
544        pub fn from_problem<B: Backend>(
545            Problem {
546                uri,
547                sub_problem,
548                autogradable,
549                points,
550                title,
551                preconditions,
552                objectives,
553                children,
554                ..
555            }: &Problem<Checked>,
556            backend: &B, //&mut StringPresenter<'_,B>,
557            css: &mut VecSet<CSS>,
558        ) -> Self {
559            let mut uses = VecSet::new();
560            let mut imports = VecSet::new();
561            let title = title.and_then(|r| {
562                if let Some((c, s)) = backend.get_html_fragment(uri.document(), r) {
563                    if s.trim().is_empty() {
564                        None
565                    } else {
566                        for c in c {
567                            css.insert(c)
568                        }
569                        Some(s)
570                    }
571                } else {
572                    None
573                }
574            });
575            let children =
576                OMDocDocumentElement::do_children(backend, children, &mut uses, &mut imports, css);
577            Self {
578                sub_problem: *sub_problem,
579                autogradable: *autogradable,
580                points: *points,
581                preconditions: preconditions.to_vec(),
582                objectives: objectives.to_vec(),
583                title,
584                uri: uri.clone(),
585                uses,
586                children,
587            }
588        }
589    }
590
591    impl OMDocVariable {
592        pub fn from_variable<B: Backend>(
593            Variable {
594                uri,
595                arity,
596                macroname,
597                tp,
598                df,
599                is_seq,
600                ..
601            }: &Variable,
602            _backend: &B, //&mut StringPresenter<'_,B>,
603        ) -> Self {
604            Self {
605                uri: uri.clone(),
606                arity: arity.clone(),
607                macro_name: macroname.as_ref().map(ToString::to_string),
608                tp: tp.clone(), //.as_ref().and_then(|t| backend.present(t).ok()), // TODO
609                df: df.clone(), //.as_ref().and_then(|t| backend.present(t).ok()), // TODO
610                is_seq: *is_seq,
611            }
612        }
613    }
614
615    impl OMDocDocumentElement {
616        pub fn from_element<B: Backend>(
617            e: &DocumentElement<Checked>,
618            backend: &B, //&mut StringPresenter<'_,B>,
619            css: &mut VecSet<CSS>,
620        ) -> Option<Self> {
621            match e {
622                DocumentElement::Section(s) => {
623                    Some(OMDocSection::from_section(s, backend, css).into())
624                }
625                DocumentElement::Paragraph(p) => {
626                    Some(OMDocParagraph::from_paragraph(p, backend, css).into())
627                }
628                DocumentElement::Problem(p) => {
629                    Some(OMDocProblem::from_problem(p, backend, css).into())
630                }
631                _ => None,
632            }
633        }
634
635        fn do_children<B: Backend>(
636            backend: &B, //&mut StringPresenter<'_,B>,
637            children: &[DocumentElement<Checked>],
638            uses: &mut VecSet<ModuleURI>,
639            imports: &mut VecSet<ModuleURI>,
640            css: &mut VecSet<CSS>,
641        ) -> Vec<Self> {
642            let mut ret = Vec::new();
643            for c in children {
644                match c {
645                    DocumentElement::SkipSection(s) => {
646                        ret.extend(Self::do_children(backend, s, uses, imports, css).into_iter())
647                    }
648                    DocumentElement::Section(s) => {
649                        ret.push(OMDocSection::from_section(s, backend, css).into());
650                    }
651                    DocumentElement::Slide { children, uri, .. } => {
652                        ret.push(OMDocSlide::from_slide(children, uri, backend, css).into())
653                    }
654                    DocumentElement::Paragraph(p) => {
655                        ret.push(OMDocParagraph::from_paragraph(p, backend, css).into());
656                    }
657                    DocumentElement::Problem(p) => {
658                        ret.push(OMDocProblem::from_problem(p, backend, css).into());
659                    }
660                    DocumentElement::Module {
661                        module, children, ..
662                    } => {
663                        let uri = module.id().into_owned();
664                        let (metatheory, signature) =
665                            if let Some(ModuleLike::Module(m)) = module.get() {
666                                (
667                                    m.meta().map(|c| c.id().into_owned()),
668                                    m.signature().map(|c| c.id().into_owned()),
669                                )
670                            } else {
671                                (None, None)
672                            };
673                        let mut uses = VecSet::new();
674                        let mut imports = VecSet::new();
675                        let children =
676                            Self::do_children(backend, children, &mut uses, &mut imports, css);
677                        ret.push(Self::Module(OMDocModule {
678                            uri,
679                            imports,
680                            uses,
681                            metatheory,
682                            signature,
683                            children,
684                        }));
685                    }
686                    DocumentElement::Morphism {
687                        morphism, children, ..
688                    } => {
689                        let uri = morphism.id().into_owned();
690                        let (total, target) = morphism.get().map_or((false, None), |m| {
691                            (m.as_ref().total, Some(m.as_ref().domain.id().into_owned()))
692                        });
693                        let mut uses = VecSet::new();
694                        let mut imports = VecSet::new();
695                        let children =
696                            Self::do_children(backend, children, &mut uses, &mut imports, css);
697                        ret.push(Self::Morphism(OMDocMorphism {
698                            uri,
699                            total,
700                            target,
701                            uses,
702                            children,
703                        }));
704                    }
705                    DocumentElement::MathStructure {
706                        structure,
707                        children,
708                        ..
709                    } => {
710                        let uri = structure.id().into_owned();
711                        let macroname = structure
712                            .get()
713                            .and_then(|s| s.as_ref().macroname.as_ref().map(ToString::to_string));
714                        let extensions = super::super::froms::get_extensions(backend, &uri)
715                            .map(|e| {
716                                (
717                                    e.as_ref().uri.clone(),
718                                    e.as_ref()
719                                        .elements
720                                        .iter()
721                                        .filter_map(|e| {
722                                            if let OpenDeclaration::Symbol(s) = e {
723                                                Some(OMDocSymbol::from_symbol(s, backend))
724                                            } else {
725                                                None
726                                            }
727                                        })
728                                        .collect(),
729                                )
730                            })
731                            .collect();
732                        let mut uses = VecSet::new();
733                        let mut imports = VecSet::new();
734                        let children =
735                            Self::do_children(backend, children, &mut uses, &mut imports, css);
736                        ret.push(Self::Structure(OMDocStructure {
737                            uri,
738                            macro_name: macroname,
739                            uses,
740                            extends: imports,
741                            children,
742                            extensions,
743                        }));
744                    }
745                    DocumentElement::Extension {
746                        extension,
747                        target,
748                        children,
749                        ..
750                    } => {
751                        let target = target.id().into_owned();
752                        let uri = extension.id().into_owned();
753                        let mut uses = VecSet::new();
754                        let mut imports = VecSet::new();
755                        let children =
756                            Self::do_children(backend, children, &mut uses, &mut imports, css);
757                        ret.push(Self::Extension(OMDocExtension {
758                            uri,
759                            target,
760                            uses,
761                            children,
762                        }));
763                    }
764                    DocumentElement::DocumentReference { target, .. } => {
765                        let title = target
766                            .get()
767                            .and_then(|d| d.title().map(ToString::to_string));
768                        let uri = target.id().into_owned();
769                        ret.push(Self::DocumentReference { uri, title });
770                    }
771                    DocumentElement::SymbolDeclaration(s) => {
772                        ret.push(Self::SymbolDeclaration(s.get().map_or_else(
773                            || either::Left(s.id().into_owned()),
774                            |s| either::Right(OMDocSymbol::from_symbol(s.as_ref(), backend)),
775                        )));
776                    }
777                    DocumentElement::Variable(v) => {
778                        ret.push(OMDocVariable::from_variable(v, backend).into());
779                    }
780                    DocumentElement::UseModule(m) => {
781                        uses.insert(m.id().into_owned());
782                    }
783                    DocumentElement::ImportModule(m) => {
784                        imports.insert(m.id().into_owned());
785                    }
786                    DocumentElement::TopTerm { term, uri, .. } => {
787                        ret.push(Self::TopTerm {
788                            term: term.clone(),
789                            uri: uri.clone(),
790                        }); //backend.present(term).unwrap_or_else(|e| format!("<mtext>term presenting failed: {e:?}</mtext>"))))
791                    }
792                    DocumentElement::Definiendum { .. }
793                    | DocumentElement::SymbolReference { .. }
794                    | DocumentElement::VariableReference { .. }
795                    | DocumentElement::Notation { .. }
796                    | DocumentElement::VariableNotation { .. }
797                    | DocumentElement::SetSectionLevel(_) => (),
798                }
799            }
800            ret
801        }
802    }
803
804    impl OMDocDocument {
805        pub fn from_document<B: Backend>(
806            d: &Document,
807            backend: &B, //&mut StringPresenter<'_,B>,
808            css: &mut VecSet<CSS>,
809        ) -> Self {
810            let uri = d.uri().clone();
811            let title = d.title().map(ToString::to_string);
812            let mut uses = VecSet::new();
813            let mut imports = VecSet::new();
814            let children = OMDocDocumentElement::do_children(
815                backend,
816                d.children(),
817                &mut uses,
818                &mut imports,
819                css,
820            );
821            Self {
822                uri,
823                title,
824                uses,
825                children,
826            }
827        }
828    }
829}