mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 16:56:36 -04:00
typeanswer: fix cleanup
Fix: Add prepare_expected back in for the 'nothing typed' & 'correctly typed' cases. This also makes expected_original redundant again. Style: %s/provided/typed/g Style: rename one ch → c Testcase: whitespace_is_trimmed: added a check for the "correctly typed" path and renamed it to tags_removed (there's no whitespace?) Testcase: empty_input_shows_as_code: changed to also check that tags get trimmed
This commit is contained in:
parent
1b7390f22d
commit
5f04afe891
1 changed files with 34 additions and 32 deletions
|
@ -33,79 +33,77 @@ macro_rules! format_typeans {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public API
|
// Public API
|
||||||
pub fn compare_answer(expected: &str, provided: &str) -> String {
|
pub fn compare_answer(expected: &str, typed: &str) -> String {
|
||||||
if provided.is_empty() {
|
if typed.is_empty() {
|
||||||
format_typeans!(htmlescape::encode_minimal(expected))
|
format_typeans!(htmlescape::encode_minimal(&prepare_expected(expected)))
|
||||||
} else {
|
} else {
|
||||||
Diff::new(expected, provided).to_html()
|
Diff::new(expected, typed).to_html()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Diff {
|
struct Diff {
|
||||||
provided: Vec<char>,
|
typed: Vec<char>,
|
||||||
expected: Vec<char>,
|
expected: Vec<char>,
|
||||||
expected_original: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Diff {
|
impl Diff {
|
||||||
fn new(expected: &str, provided: &str) -> Self {
|
fn new(expected: &str, typed: &str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
provided: normalize_to_nfc(provided).chars().collect(),
|
typed: normalize_to_nfc(typed).chars().collect(),
|
||||||
expected: normalize_to_nfc(&prepare_expected(expected))
|
expected: normalize_to_nfc(&prepare_expected(expected))
|
||||||
.chars()
|
.chars()
|
||||||
.collect(),
|
.collect(),
|
||||||
expected_original: expected.to_string(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Entry Point
|
// Entry Point
|
||||||
fn to_html(&self) -> String {
|
fn to_html(&self) -> String {
|
||||||
if self.provided == self.expected {
|
if self.typed == self.expected {
|
||||||
format_typeans!(format!(
|
format_typeans!(format!(
|
||||||
"<span class=typeGood>{}</span>",
|
"<span class=typeGood>{}</span>",
|
||||||
self.expected_original
|
&self.expected.iter().collect::<String>()
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
let output = self.to_tokens();
|
let output = self.to_tokens();
|
||||||
let provided_html = render_tokens(&output.provided_tokens);
|
let typed_html = render_tokens(&output.typed_tokens);
|
||||||
let expected_html = render_tokens(&output.expected_tokens);
|
let expected_html = render_tokens(&output.expected_tokens);
|
||||||
|
|
||||||
format_typeans!(format!(
|
format_typeans!(format!(
|
||||||
"{provided_html}<br><span id=typearrow>↓</span><br>{expected_html}"
|
"{typed_html}<br><span id=typearrow>↓</span><br>{expected_html}"
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_tokens(&self) -> DiffTokens {
|
fn to_tokens(&self) -> DiffTokens {
|
||||||
let mut matcher = SequenceMatcher::new(&self.provided, &self.expected);
|
let mut matcher = SequenceMatcher::new(&self.typed, &self.expected);
|
||||||
let mut provided_tokens = Vec::new();
|
let mut typed_tokens = Vec::new();
|
||||||
let mut expected_tokens = Vec::new();
|
let mut expected_tokens = Vec::new();
|
||||||
|
|
||||||
for opcode in matcher.get_opcodes() {
|
for opcode in matcher.get_opcodes() {
|
||||||
let provided_slice = slice(&self.provided, opcode.first_start, opcode.first_end);
|
let typed_slice = slice(&self.typed, opcode.first_start, opcode.first_end);
|
||||||
let expected_slice = slice(&self.expected, opcode.second_start, opcode.second_end);
|
let expected_slice = slice(&self.expected, opcode.second_start, opcode.second_end);
|
||||||
|
|
||||||
match opcode.tag.as_str() {
|
match opcode.tag.as_str() {
|
||||||
"equal" => {
|
"equal" => {
|
||||||
provided_tokens.push(DiffToken::good(provided_slice));
|
typed_tokens.push(DiffToken::good(typed_slice));
|
||||||
expected_tokens.push(DiffToken::good(expected_slice));
|
expected_tokens.push(DiffToken::good(expected_slice));
|
||||||
}
|
}
|
||||||
"delete" => provided_tokens.push(DiffToken::bad(provided_slice)),
|
"delete" => typed_tokens.push(DiffToken::bad(typed_slice)),
|
||||||
"insert" => {
|
"insert" => {
|
||||||
provided_tokens.push(DiffToken::missing(
|
typed_tokens.push(DiffToken::missing(
|
||||||
"-".repeat(expected_slice.chars().count()),
|
"-".repeat(expected_slice.chars().count()),
|
||||||
));
|
));
|
||||||
expected_tokens.push(DiffToken::missing(expected_slice));
|
expected_tokens.push(DiffToken::missing(expected_slice));
|
||||||
}
|
}
|
||||||
"replace" => {
|
"replace" => {
|
||||||
provided_tokens.push(DiffToken::bad(provided_slice));
|
typed_tokens.push(DiffToken::bad(typed_slice));
|
||||||
expected_tokens.push(DiffToken::missing(expected_slice));
|
expected_tokens.push(DiffToken::missing(expected_slice));
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DiffTokens {
|
DiffTokens {
|
||||||
provided_tokens,
|
typed_tokens,
|
||||||
expected_tokens,
|
expected_tokens,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -139,7 +137,7 @@ fn isolate_leading_mark(text: &str) -> Cow<str> {
|
||||||
if text
|
if text
|
||||||
.chars()
|
.chars()
|
||||||
.next()
|
.next()
|
||||||
.map_or(false, |ch| GeneralCategory::of(ch).is_mark())
|
.map_or(false, |c| GeneralCategory::of(c).is_mark())
|
||||||
{
|
{
|
||||||
format!("\u{a0}{text}").into()
|
format!("\u{a0}{text}").into()
|
||||||
} else {
|
} else {
|
||||||
|
@ -149,7 +147,7 @@ fn isolate_leading_mark(text: &str) -> Cow<str> {
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
struct DiffTokens {
|
struct DiffTokens {
|
||||||
provided_tokens: Vec<DiffToken>,
|
typed_tokens: Vec<DiffToken>,
|
||||||
expected_tokens: Vec<DiffToken>,
|
expected_tokens: Vec<DiffToken>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,7 +210,7 @@ mod test {
|
||||||
let ctx = Diff::new("¿Y ahora qué vamos a hacer?", "y ahora qe vamosa hacer");
|
let ctx = Diff::new("¿Y ahora qué vamos a hacer?", "y ahora qe vamosa hacer");
|
||||||
let output = ctx.to_tokens();
|
let output = ctx.to_tokens();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
output.provided_tokens,
|
output.typed_tokens,
|
||||||
vec![
|
vec![
|
||||||
bad("y"),
|
bad("y"),
|
||||||
good(" ahora q"),
|
good(" ahora q"),
|
||||||
|
@ -245,18 +243,18 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn missed_chars_only_shown_in_provided_when_after_good() {
|
fn missed_chars_only_shown_in_typed_when_after_good() {
|
||||||
let ctx = Diff::new("1", "23");
|
let ctx = Diff::new("1", "23");
|
||||||
assert_eq!(ctx.to_tokens().provided_tokens, &[bad("23")]);
|
assert_eq!(ctx.to_tokens().typed_tokens, &[bad("23")]);
|
||||||
let ctx = Diff::new("12", "1");
|
let ctx = Diff::new("12", "1");
|
||||||
assert_eq!(ctx.to_tokens().provided_tokens, &[good("1"), missing("-"),]);
|
assert_eq!(ctx.to_tokens().typed_tokens, &[good("1"), missing("-"),]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn missed_chars_counted_correctly() {
|
fn missed_chars_counted_correctly() {
|
||||||
let ctx = Diff::new("нос", "нс");
|
let ctx = Diff::new("нос", "нс");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ctx.to_tokens().provided_tokens,
|
ctx.to_tokens().typed_tokens,
|
||||||
&[good("н"), missing("-"), good("с")]
|
&[good("н"), missing("-"), good("с")]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -266,7 +264,7 @@ mod test {
|
||||||
// this was not parsed as expected with dissimilar 1.0.4
|
// this was not parsed as expected with dissimilar 1.0.4
|
||||||
let ctx = Diff::new("쓰다듬다", "스다뜸다");
|
let ctx = Diff::new("쓰다듬다", "스다뜸다");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ctx.to_tokens().provided_tokens,
|
ctx.to_tokens().typed_tokens,
|
||||||
&[bad("스"), good("다"), bad("뜸"), good("다"),]
|
&[bad("스"), good("다"), bad("뜸"), good("다"),]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -285,13 +283,17 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn whitespace_is_trimmed() {
|
fn tags_removed() {
|
||||||
assert_eq!(prepare_expected("<div>foo</div>"), "foo");
|
assert_eq!(prepare_expected("<div>123</div>"), "123");
|
||||||
|
assert_eq!(
|
||||||
|
Diff::new("<div>123</div>", "123").to_html(),
|
||||||
|
"<code id=typeans><span class=typeGood>123</span></code>"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty_input_shows_as_code() {
|
fn empty_input_shows_as_code() {
|
||||||
let ctx = compare_answer("123", "");
|
let ctx = compare_answer("<div>123</div>", "");
|
||||||
assert_eq!(ctx, "<code id=typeans>123</code>");
|
assert_eq!(ctx, "<code id=typeans>123</code>");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue