From c4a19803690de1a7fb85c6c4387af207e4b90401 Mon Sep 17 00:00:00 2001 From: bolphen <> Date: Wed, 12 Jul 2023 05:27:24 +0000 Subject: [PATCH 1/3] Mark individual incorrect characters --- src/ui.rs | 67 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/src/ui.rs b/src/ui.rs index c06f801..0ed8f49 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -115,40 +115,53 @@ impl ThemedWidget for &Test { })) // current word .chain({ - let progress_ind = self.words[self.current_word] - .progress - .len() - .min(self.words[self.current_word].text.len()); - - let correct = self.words[self.current_word] - .text - .starts_with(&self.words[self.current_word].progress[..]); - - let (typed, untyped) = - self.words[self.current_word] - .text - .split_at(ceil_char_boundary( - &self.words[self.current_word].text, - progress_ind, - )); - - let mut remaining = untyped.chars().chain(iter::once(' ')); - let cursor = remaining.next().unwrap(); + let text = &self.words[self.current_word].text; + let prog = &self.words[self.current_word].progress; + let text_count = text.chars().count(); + let prog_count = prog.chars().count(); + + // Find the index for the first incorrect character + let mut incorrect_ind = 0; + for (idx, chr) in prog.char_indices() { + if !text.get(idx..).unwrap().starts_with(chr) { + break; + } + incorrect_ind = idx + chr.len_utf8(); + } + + let (cursor_ind, chr) = text.char_indices() + .nth(prog_count.min(text_count)) + .unwrap_or((text.len(), '\0')); + let untyped_ind = cursor_ind + chr.len_utf8(); iter::once(vec![ Span::styled( - typed, - if correct { - theme.prompt_current_correct - } else { - theme.prompt_current_incorrect - }, + text.get(..incorrect_ind).unwrap(), + theme.prompt_current_correct, + ), + Span::styled( + text.get(incorrect_ind..cursor_ind).unwrap(), + theme.prompt_current_incorrect, ), Span::styled( - cursor.to_string(), + text.get(cursor_ind..untyped_ind).unwrap_or(""), theme.prompt_current_untyped.patch(theme.prompt_cursor), ), - Span::styled(remaining.collect::(), theme.prompt_current_untyped), + Span::styled( + text.get(untyped_ind..).unwrap_or(""), + theme.prompt_current_untyped, + ), + Span::styled( + " ", // Delimiting space + if prog_count > text_count { + // There are redundant letters at the end + theme.prompt_current_incorrect.patch(theme.prompt_cursor) + } else if prog_count == text_count { + theme.prompt_current_untyped.patch(theme.prompt_cursor) + } else { + theme.prompt_current_untyped + }, + ), ]) }) // remaining words From 908603e522fe3df7e6628de9a3a3d47258ce8c16 Mon Sep 17 00:00:00 2001 From: bolphen <> Date: Thu, 28 Sep 2023 19:17:41 +0000 Subject: [PATCH 2/3] Remove unused code --- src/ui.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/ui.rs b/src/ui.rs index 0ed8f49..4217c02 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -341,12 +341,3 @@ impl ThemedWidget for &results::Results { wpm_chart.render(res_chunks[1], buf); } } - -// FIXME: replace with `str::ceil_char_boundary` when stable -fn ceil_char_boundary(string: &str, index: usize) -> usize { - if string.is_char_boundary(index) { - index - } else { - ceil_char_boundary(string, index + 1) - } -} From 685ed69aa1be415770e0fa3ba888b77fb4b37aaf Mon Sep 17 00:00:00 2001 From: bolphen <> Date: Thu, 28 Sep 2023 21:27:47 +0000 Subject: [PATCH 3/3] Mark individual incorrect characters, take II --- src/ui.rs | 138 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 84 insertions(+), 54 deletions(-) diff --git a/src/ui.rs b/src/ui.rs index 4217c02..da70a88 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -104,65 +104,95 @@ impl ThemedWidget for &Test { let words = iter::empty::>() // already typed words .chain(self.words[..self.current_word].iter().map(|w| { - vec![Span::styled( - w.text.clone() + " ", - if w.progress == w.text { - theme.prompt_correct + let text = &mut w.text.chars(); + let prog = &mut w.progress.chars(); + let mut display = vec![]; + 'checking: loop { + if let Some(t) = text.next() { + if let Some(p) = prog.next() { + display.push( + Span::styled( + t.to_string(), + if t == p { + theme.prompt_correct + } else { + theme.prompt_incorrect + } + ), + ); + } else { + display.push( + Span::styled(t.to_string(), theme.prompt_untyped), + ); + } } else { - theme.prompt_incorrect - }, - )] + if let Some(p) = prog.next() { + display.push( + Span::styled(p.to_string(), theme.prompt_incorrect), + ); + } else { + display.push( + Span::styled(" ", theme.prompt_untyped), + ); + break 'checking; + } + } + } + display })) // current word .chain({ - let text = &self.words[self.current_word].text; - let prog = &self.words[self.current_word].progress; - let text_count = text.chars().count(); - let prog_count = prog.chars().count(); - - // Find the index for the first incorrect character - let mut incorrect_ind = 0; - for (idx, chr) in prog.char_indices() { - if !text.get(idx..).unwrap().starts_with(chr) { - break; - } - incorrect_ind = idx + chr.len_utf8(); - } - - let (cursor_ind, chr) = text.char_indices() - .nth(prog_count.min(text_count)) - .unwrap_or((text.len(), '\0')); - let untyped_ind = cursor_ind + chr.len_utf8(); - - iter::once(vec![ - Span::styled( - text.get(..incorrect_ind).unwrap(), - theme.prompt_current_correct, - ), - Span::styled( - text.get(incorrect_ind..cursor_ind).unwrap(), - theme.prompt_current_incorrect, - ), - Span::styled( - text.get(cursor_ind..untyped_ind).unwrap_or(""), - theme.prompt_current_untyped.patch(theme.prompt_cursor), - ), - Span::styled( - text.get(untyped_ind..).unwrap_or(""), - theme.prompt_current_untyped, - ), - Span::styled( - " ", // Delimiting space - if prog_count > text_count { - // There are redundant letters at the end - theme.prompt_current_incorrect.patch(theme.prompt_cursor) - } else if prog_count == text_count { - theme.prompt_current_untyped.patch(theme.prompt_cursor) + let text = &mut self.words[self.current_word].text.chars(); + let prog = &mut self.words[self.current_word].progress.chars(); + let mut display = vec![]; + let mut cursor_showed = false; + 'checking: loop { + if let Some(t) = text.next() { + if let Some(p) = prog.next() { + display.push( + Span::styled( + t.to_string(), + if t == p { + theme.prompt_current_correct + } else { + theme.prompt_current_incorrect + } + ), + ); } else { - theme.prompt_current_untyped - }, - ), - ]) + display.push( + Span::styled( + t.to_string(), + if cursor_showed { + theme.prompt_current_untyped + } else { + cursor_showed = true; + theme.prompt_current_untyped.patch(theme.prompt_cursor) + } + ), + ); + } + } else { + if let Some(p) = prog.next() { + display.push( + Span::styled(p.to_string(), theme.prompt_current_incorrect), + ); + } else { + display.push( + Span::styled( + " ", + if cursor_showed { + theme.prompt_current_untyped + } else { + theme.prompt_current_untyped.patch(theme.prompt_cursor) + } + ), + ); + break 'checking; + } + } + }; + iter::once(display) }) // remaining words .chain(