mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 14:02:21 -04:00
Modify card rendering output to specify if rendered card is empty (#3890)
* modify render_card to return whether card was empty * plumbing * add flag to proto message * plumbing: pass flag along to PartiallyRenderedCard * add tests * Use a custom return type for clarity (dae)
This commit is contained in:
parent
1798620d64
commit
ccab18b7ba
5 changed files with 39 additions and 14 deletions
|
@ -127,6 +127,7 @@ message RenderCardResponse {
|
||||||
repeated RenderedTemplateNode answer_nodes = 2;
|
repeated RenderedTemplateNode answer_nodes = 2;
|
||||||
string css = 3;
|
string css = 3;
|
||||||
bool latex_svg = 4;
|
bool latex_svg = 4;
|
||||||
|
bool is_empty = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message RenderedTemplateNode {
|
message RenderedTemplateNode {
|
||||||
|
|
|
@ -60,6 +60,7 @@ class PartiallyRenderedCard:
|
||||||
anodes: TemplateReplacementList
|
anodes: TemplateReplacementList
|
||||||
css: str
|
css: str
|
||||||
latex_svg: bool
|
latex_svg: bool
|
||||||
|
is_empty: bool
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_proto(
|
def from_proto(
|
||||||
|
@ -68,7 +69,9 @@ class PartiallyRenderedCard:
|
||||||
qnodes = cls.nodes_from_proto(out.question_nodes)
|
qnodes = cls.nodes_from_proto(out.question_nodes)
|
||||||
anodes = cls.nodes_from_proto(out.answer_nodes)
|
anodes = cls.nodes_from_proto(out.answer_nodes)
|
||||||
|
|
||||||
return PartiallyRenderedCard(qnodes, anodes, out.css, out.latex_svg)
|
return PartiallyRenderedCard(
|
||||||
|
qnodes, anodes, out.css, out.latex_svg, out.is_empty
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def nodes_from_proto(
|
def nodes_from_proto(
|
||||||
|
|
|
@ -219,6 +219,7 @@ impl From<RenderCardOutput> for anki_proto::card_rendering::RenderCardResponse {
|
||||||
answer_nodes: rendered_nodes_to_proto(o.anodes),
|
answer_nodes: rendered_nodes_to_proto(o.anodes),
|
||||||
css: o.css,
|
css: o.css,
|
||||||
latex_svg: o.latex_svg,
|
latex_svg: o.latex_svg,
|
||||||
|
is_empty: o.is_empty,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ pub struct RenderCardOutput {
|
||||||
pub anodes: Vec<RenderedNode>,
|
pub anodes: Vec<RenderedNode>,
|
||||||
pub css: String,
|
pub css: String,
|
||||||
pub latex_svg: bool,
|
pub latex_svg: bool,
|
||||||
|
pub is_empty: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderCardOutput {
|
impl RenderCardOutput {
|
||||||
|
@ -136,7 +137,7 @@ impl Collection {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let (qnodes, anodes) = render_card(RenderCardRequest {
|
let response = render_card(RenderCardRequest {
|
||||||
qfmt,
|
qfmt,
|
||||||
afmt,
|
afmt,
|
||||||
field_map: &field_map,
|
field_map: &field_map,
|
||||||
|
@ -147,10 +148,11 @@ impl Collection {
|
||||||
partial_render,
|
partial_render,
|
||||||
})?;
|
})?;
|
||||||
Ok(RenderCardOutput {
|
Ok(RenderCardOutput {
|
||||||
qnodes,
|
qnodes: response.qnodes,
|
||||||
anodes,
|
anodes: response.anodes,
|
||||||
css: nt.config.css.clone(),
|
css: nt.config.css.clone(),
|
||||||
latex_svg: nt.config.latex_svg,
|
latex_svg: nt.config.latex_svg,
|
||||||
|
is_empty: response.is_empty,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -592,6 +592,13 @@ pub struct RenderCardRequest<'a> {
|
||||||
pub partial_render: bool,
|
pub partial_render: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct RenderCardResponse {
|
||||||
|
pub qnodes: Vec<RenderedNode>,
|
||||||
|
pub anodes: Vec<RenderedNode>,
|
||||||
|
pub is_empty: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `(qnodes, anodes, is_empty)`
|
||||||
pub fn render_card(
|
pub fn render_card(
|
||||||
RenderCardRequest {
|
RenderCardRequest {
|
||||||
qfmt,
|
qfmt,
|
||||||
|
@ -603,7 +610,7 @@ pub fn render_card(
|
||||||
tr,
|
tr,
|
||||||
partial_render: partial_for_python,
|
partial_render: partial_for_python,
|
||||||
}: RenderCardRequest<'_>,
|
}: RenderCardRequest<'_>,
|
||||||
) -> Result<(Vec<RenderedNode>, Vec<RenderedNode>)> {
|
) -> Result<RenderCardResponse> {
|
||||||
// prepare context
|
// prepare context
|
||||||
let mut context = RenderContext {
|
let mut context = RenderContext {
|
||||||
fields: field_map,
|
fields: field_map,
|
||||||
|
@ -638,7 +645,11 @@ pub fn render_card(
|
||||||
};
|
};
|
||||||
if let Some(text) = empty_message {
|
if let Some(text) = empty_message {
|
||||||
qnodes.push(RenderedNode::Text { text: text.clone() });
|
qnodes.push(RenderedNode::Text { text: text.clone() });
|
||||||
return Ok((qnodes, vec![RenderedNode::Text { text }]));
|
return Ok(RenderCardResponse {
|
||||||
|
qnodes,
|
||||||
|
anodes: vec![RenderedNode::Text { text }],
|
||||||
|
is_empty: true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// answer side
|
// answer side
|
||||||
|
@ -654,7 +665,11 @@ pub fn render_card(
|
||||||
.and_then(|tmpl| tmpl.render(&context, tr))
|
.and_then(|tmpl| tmpl.render(&context, tr))
|
||||||
.map_err(|e| template_error_to_anki_error(e, false, browser, tr))?;
|
.map_err(|e| template_error_to_anki_error(e, false, browser, tr))?;
|
||||||
|
|
||||||
Ok((qnodes, anodes))
|
Ok(RenderCardResponse {
|
||||||
|
qnodes,
|
||||||
|
anodes,
|
||||||
|
is_empty: false,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cloze_is_empty(field_map: &HashMap<&str, Cow<str>>, card_ord: u16) -> bool {
|
fn cloze_is_empty(field_map: &HashMap<&str, Cow<str>>, card_ord: u16) -> bool {
|
||||||
|
@ -1338,14 +1353,15 @@ mod test {
|
||||||
tr: &tr,
|
tr: &tr,
|
||||||
partial_render: true,
|
partial_render: true,
|
||||||
};
|
};
|
||||||
let qnodes = super::render_card(req.clone()).unwrap().0;
|
let response = super::render_card(req.clone()).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
qnodes[0],
|
response.qnodes[0],
|
||||||
FN::Text {
|
FN::Text {
|
||||||
text: "test".into()
|
text: "test".into()
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
if let FN::Text { ref text } = qnodes[1] {
|
assert!(response.is_empty);
|
||||||
|
if let FN::Text { ref text } = response.qnodes[1] {
|
||||||
assert!(text.contains("card is blank"));
|
assert!(text.contains("card is blank"));
|
||||||
} else {
|
} else {
|
||||||
unreachable!();
|
unreachable!();
|
||||||
|
@ -1354,9 +1370,9 @@ mod test {
|
||||||
// a popular card template expects {{FrontSide}} to resolve to an empty
|
// a popular card template expects {{FrontSide}} to resolve to an empty
|
||||||
// string on the front side :-(
|
// string on the front side :-(
|
||||||
req.qfmt = "{{FrontSide}}{{N}}";
|
req.qfmt = "{{FrontSide}}{{N}}";
|
||||||
let qnodes = super::render_card(req.clone()).unwrap().0;
|
let response = super::render_card(req.clone()).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&qnodes,
|
&response.qnodes,
|
||||||
&[
|
&[
|
||||||
FN::Replacement {
|
FN::Replacement {
|
||||||
field_name: "FrontSide".into(),
|
field_name: "FrontSide".into(),
|
||||||
|
@ -1366,8 +1382,10 @@ mod test {
|
||||||
FN::Text { text: "N".into() }
|
FN::Text { text: "N".into() }
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
assert!(!response.is_empty);
|
||||||
req.partial_render = false;
|
req.partial_render = false;
|
||||||
let qnodes = super::render_card(req.clone()).unwrap().0;
|
let response = super::render_card(req.clone()).unwrap();
|
||||||
assert_eq!(&qnodes, &[FN::Text { text: "N".into() }]);
|
assert_eq!(&response.qnodes, &[FN::Text { text: "N".into() }]);
|
||||||
|
assert!(!response.is_empty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue