PoiScript / orgize

A Rust library for parsing org-mode files.
https://poiscript.github.io/orgize/
MIT License
289 stars 35 forks source link

optional support for org-fc cloze markup #79

Closed rrix closed 3 months ago

rrix commented 4 months ago

I use https://github.com/l3kn/org-fc, an anki-style flashcarding system that has a few "card types"; most of them are based on the shape of a heading (one side of the card is the heading, one is the text therein), a text-input, etc, but it also introduces a custom markup for what it calls "cloze" cards:

The cards text contains one or more holes . During review, one hole is hidden while the text of (some) remaining ones is shown.

These introduce a markup that interacts poorly with orgize's parser, especially if you put non-text syntax inside of the "holes". With the 0.9 parser, I just used a regex match on a Text block to transform these clozes in to <span> elements on export, but if a link or so is in there it breaks up the text block.

Deletions can have the following forms

  • {{text}}
  • {{text}@id}
  • {{text}{hint}}
  • {{text}{hint}@id}

It would be nice to have a (perhaps feature gated) extension to the rowan syntax to parse these and have access to the underlying tokens in the text and hint for more robust export.

PoiScript commented 4 months ago

initial support for org-fc cloze was landed in commit 6c4513d857402fa53a0ba55dd793e32bb6579881

you can give it a try now by editing you cargo.toml to

orgize = { git = "https://github.com/PoiScript/orgize", branch = "v0.10", features = [
    "syntax-org-fc",
] }

one thing that I'm not sure about is the method naming: text() -> impl Iteractor<_> - it seems a bit wired.

rrix commented 4 months ago

thanks poi, this is quite promising, i appreciate it. I'm having trouble using it within an traverse fn; I don't ever see any Event::Cloze events though i do see CLOZEs in the Document AST

    let mut handler = from_fn_with_ctx(|event, ctx| {
        match &event {
            Event::Cloze(the_cloze) => {
                println!("XXX");
                dbg!(the_cloze);

                html_export.push_str(r#"<span class="cloze" "#);
                if the_cloze.hint().is_some() {
                    html_export.push_str(format!(r#"alt="{}""#, the_cloze.hint().unwrap()));
                }
                html_export.push_str(">");
                for item in the_cloze.text() {
                    match item.into_node() {
                        Some(node) => html_export.render(&node),
                        None => {}
                    }
                }
                html_export.push_str("</span>");
            }
// ...

I think I'm using the text() iterator correctly here, except maybe i should match on .kind()? Ultimately, I'd like the links and whatnot within the text to render.

W.R.T. the naming, I don't have much advice here. You could call it answer() to differentiate it from other elements' text(), but that might be more confusing than helpful.

PoiScript commented 3 months ago

hello, sorry for the late response. the traverser now emits cloze events properly:

let org = Org::parse("{{cloze1}{hint1}}");

let mut cloze = None;

let mut t = from_fn(|event| {
    if let Event::Cloze(cloze_) = event {
        cloze = Some(cloze_);
    }
});

org.traverse(&mut t);

let cloze = cloze.unwrap();

assert_eq!(cloze.text_raw(), "cloze1");
assert_eq!(cloze.hint().unwrap(), "hint1");
PoiScript commented 3 months ago

and I just checked the anki docs, and it also labels this field as "Text". so I think we're good to go.