diff --git a/qt/aqt/addcards.py b/qt/aqt/addcards.py index 363ae5c39..12b65ba7c 100644 --- a/qt/aqt/addcards.py +++ b/qt/aqt/addcards.py @@ -169,9 +169,7 @@ class AddCards(QDialog): showWarning(problem, help="AddItems#AddError") return None if note.model()["type"] == MODEL_CLOZE: - if not self.mw.col.models._availClozeOrds( - note.model(), note.joinedFields(), False - ): + if not note.cloze_numbers_in_fields(): if not askUser( _( "You have a cloze deletion note type " diff --git a/rslib/ftl/card-template-rendering.ftl b/rslib/ftl/card-template-rendering.ftl index a51acb344..42e1b66b1 100644 --- a/rslib/ftl/card-template-rendering.ftl +++ b/rslib/ftl/card-template-rendering.ftl @@ -37,3 +37,6 @@ card-template-rendering-no-such-field = # either due to a badly-designed template, or because required fields # are missing. card-template-rendering-empty-front = The front of this card is blank. + +card-template-rendering-missing-cloze = No cloze { $number } found on card. + Please either add a cloze deletion, or use the Empty Cards tool. diff --git a/rslib/src/notetype/mod.rs b/rslib/src/notetype/mod.rs index 2b9ab5e46..f948271c0 100644 --- a/rslib/src/notetype/mod.rs +++ b/rslib/src/notetype/mod.rs @@ -312,6 +312,10 @@ impl NoteType { }) .next() } + + pub(crate) fn is_cloze(&self) -> bool { + matches!(self.config.kind(), NoteTypeKind::Cloze) + } } impl From for NoteTypeProto { diff --git a/rslib/src/notetype/render.rs b/rslib/src/notetype/render.rs index 75b3f9bb7..3cd1aea50 100644 --- a/rslib/src/notetype/render.rs +++ b/rslib/src/notetype/render.rs @@ -109,7 +109,8 @@ impl Collection { ) }; - let (qnodes, anodes) = render_card(qfmt, afmt, &field_map, card.ord, &self.i18n)?; + let (qnodes, anodes) = + render_card(qfmt, afmt, &field_map, card.ord, nt.is_cloze(), &self.i18n)?; Ok(RenderCardOutput { qnodes, anodes }) } diff --git a/rslib/src/template.rs b/rslib/src/template.rs index 19d173b55..ebc5e4f2d 100644 --- a/rslib/src/template.rs +++ b/rslib/src/template.rs @@ -2,8 +2,8 @@ // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html use crate::err::{AnkiError, Result, TemplateError}; -use crate::i18n::{tr_strs, I18n, TR}; -use crate::template_filters::apply_filters; +use crate::i18n::{tr_args, tr_strs, I18n, TR}; +use crate::{cloze::add_cloze_numbers_in_string, template_filters::apply_filters}; use lazy_static::lazy_static; use nom::branch::alt; use nom::bytes::complete::{tag, take_until}; @@ -23,6 +23,8 @@ static TEMPLATE_ERROR_LINK: &str = "https://anki.tenderapp.com/kb/problems/card-template-has-a-problem"; static TEMPLATE_BLANK_LINK: &str = "https://anki.tenderapp.com/kb/card-appearance/the-front-of-this-card-is-blank"; +static TEMPLATE_BLANK_CLOZE_LINK: &str = + "https://anki.tenderapp.com/kb/problems/no-cloze-found-on-card"; // Lexing //---------------------------------------- @@ -521,6 +523,7 @@ pub fn render_card( afmt: &str, field_map: &HashMap<&str, Cow>, card_ord: u16, + is_cloze: bool, i18n: &I18n, ) -> Result<(Vec, Vec)> { // prepare context @@ -537,7 +540,20 @@ pub fn render_card( .map_err(|e| template_error_to_anki_error(e, true, i18n))?; // check if the front side was empty - if !qtmpl.renders_with_fields(context.nonempty_fields) { + if is_cloze { + if cloze_is_empty(field_map, card_ord) { + let info = format!( + "
{}
{}
", + i18n.trn( + TR::CardTemplateRenderingMissingCloze, + tr_args!["number"=>card_ord+1] + ), + TEMPLATE_BLANK_CLOZE_LINK, + i18n.tr(TR::CardTemplateRenderingMoreInfo) + ); + qnodes.push(RenderedNode::Text { text: info }); + } + } else if !qtmpl.renders_with_fields(context.nonempty_fields) { let info = format!( "
{}
{}
", i18n.tr(TR::CardTemplateRenderingEmptyFront), @@ -545,7 +561,7 @@ pub fn render_card( i18n.tr(TR::CardTemplateRenderingMoreInfo) ); qnodes.push(RenderedNode::Text { text: info }); - }; + } // answer side context.question_side = false; @@ -556,6 +572,14 @@ pub fn render_card( Ok((qnodes, anodes)) } +fn cloze_is_empty(field_map: &HashMap<&str, Cow>, card_ord: u16) -> bool { + let mut set = HashSet::with_capacity(4); + for field in field_map.values() { + add_cloze_numbers_in_string(field.as_ref(), &mut set); + } + !set.contains(&(card_ord + 1)) +} + // Field requirements //---------------------------------------- @@ -1063,7 +1087,7 @@ mod test { let i18n = I18n::new(&[""], "", log::terminal()); use crate::template::RenderedNode as FN; - let qnodes = super::render_card("test{{E}}", "", &map, 1, &i18n) + let qnodes = super::render_card("test{{E}}", "", &map, 1, false, &i18n) .unwrap() .0; assert_eq!(