Ability to generate nested cloze

It would be great if it was possible to create nested clozes. For example if I wanted to learn “The first three letters of the alphabet are abc”

I would create a nested cloze for {{c4:: {{c1::a}}{{c2::b}}{{c3::c}} }} . Which would generate four, three where only one letter is hidden, and one where all are hidden

3 Likes

I just wanted to bump this thread and also ask the devs if a PR to this end would be welcome? From glancing at the code it seems it would only require some changes in clozes.rs?

2 Likes

While this feels like a bit of a niche feature to me, if the PR is clean and does not introduce regressions, I can’t see a strong reason for rejecting it.

3 Likes

Ok, I have started to sketch on one, will make a PR once I’ve had the time to finish it.

2 Likes

@TRIAEIOU you are evil I wanted to contribute to anki :frowning: /s

let string1 =
   "The war was {{c3::{{c2::19::it was the last century}}{{c1::42}}}}";
let string2 = "The war was {{c2::{{c3::19}}{{c1::42}}}}";
let string3 = "The war was {{c1::{{c2::19}}{{c3::42}}}}";
let string4 =
   "The first world war begon {{c1::{{c2::19}}{{c3::36}}}} and ended {{c4::{{c5::19}}{{c6::42}}}}";
let string5 = "This is a {{c2::cloze}} not a {{c1::question}}";
let string6 = "This is a {{c1::cloze}} not a {{c2::question}}";

let is_nested_cloze = /{{c\d+::.*?}}}}/g;
let remove_cloze = /{{c\d+::|((::.*?)?}})/g;
let get_cloze_answer = /(?<={{c\d+::).*?((?=::)|(?=}}))/g;

function generate_cloze(string) {
   let nested_cloze = string.match(is_nested_cloze);
   if (nested_cloze) {
      for (let i = 0; i < nested_cloze.length; i++) {
         let clozes = nested_cloze[i].slice(6);
         clozes = clozes.match(get_cloze_answer);
         console.log({ clozes });
         clozes.forEach((answer) => {
            let get_cloze = new RegExp(`({{c\\d+::)${answer}.*?(}})`, "g");
            let match = string.match(get_cloze);
            let question = string.replaceAll(get_cloze, "[...]");
            question = question.replaceAll(remove_cloze, "");
            console.log({ question, answer, match, get_cloze });
         });
         let answer = clozes.reduce((result, element) => result + element, "");
         let question = string.replace(nested_cloze, "[...]");
         question = question.replaceAll(remove_cloze, "");
         console.log({ nested_cloze, answer, question });
      }
   }
}

generate_cloze(string1);
generate_cloze(string2);
generate_cloze(string3);
generate_cloze(string4);
generate_cloze(string5);
generate_cloze(string6);

here is someone who has already done it if you insist on writing it

2 Likes

Unless I am mistaken that is JavaScript, so for “client side” parsing of the note {{Text}}?

I was looking at doing it in the rust backend, through a simplistic state machine to parse the clozes in cloze.rs.

Cheers

1 Like

I also planned of doing it in rust. But I used javascript to prototype the algorithm. Since programming with rust takes more time.

1 Like

If you want to do it you are more than welcome to run with the ball :slight_smile: I simply wanted the functionality! FWIW I am not sure regex is the way to go, as far as I understand the rust implementation lacks recursive patterns which I think would be needed to make it clean. Let me know if you decide to run with it - then I wont.

Cheers

1 Like

I am struggling a lot. It would be better if you implemented it.

2 Likes

Love this, I’ve been waiting for this for years :slight_smile:

3 Likes

I was able to do it in javascript without using recursive patterns. If you want you can convert it to rust, just mention me. nested_cloze_deletion/index.js at main · Vilhelm-Ian/nested_cloze_deletion · GitHub

2 Likes

Thanks but there’s already an accepted PR which will be included in 2.1.56 (Nested clozes and increased cloze meta data by TRIAEIOU · Pull Request #2141 · ankitects/anki · GitHub). To be clear, I may have started the PR but practically all of the code is Damien’s.

Cheers!

5 Likes

thank you very much. I am excited for the feature. What do you think of my approach

Looks good, it’s a similar approach to what’s in the PR. I don’t know how efficient slice and other substring functions are in JS so I don’t know if there are any performance gains to be made by reducing the amount of string copying.

1 Like

Another interesting feature would be to be able to cloze a piece of text on multiple cards, f.e. like: {{c1,c2::this cloze}} would be hidden on card 1 and 2 and {{c2,c3::this}} on card 2 and 3.

Edit: although you can probably do that by nesting them, which is just a bit less handy.

1 Like