Skip to content

Commit

Permalink
feat: handle edge cases gracefully and record spaces
Browse files Browse the repository at this point in the history
  • Loading branch information
max-niederman committed Mar 31, 2021
1 parent 6dd75b5 commit 3a265bb
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 44 deletions.
30 changes: 9 additions & 21 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "ttyper"
description = "Terminal-based typing test."
version = "0.1.11"
version = "0.1.12"
readme = "README.md"
repository = "https://github.com/max-niederman/ttyper.git"
homepage = "https://github.com/max-niederman/ttyper"
Expand Down
13 changes: 11 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,16 @@ impl Opt {
fn main() -> crossterm::Result<()> {
let opt = Opt::from_args();

let mut test = Test::new(opt.gen_contents());
let mut test = {
let contents = opt.gen_contents();

if contents.is_empty() {
println!("Test contents were empty. Exiting...");
return Ok(());
};

Test::new(contents)
};

let mut stdout = io::stdout();
execute!(
Expand Down Expand Up @@ -146,7 +155,7 @@ fn main() -> crossterm::Result<()> {
}

// Draw results
let results = Results::from(&test);
let results = Results::from(test);
terminal.clear()?;
terminal.draw(|f| {
f.render_widget(&results, f.size());
Expand Down
31 changes: 26 additions & 5 deletions src/test/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ pub struct TestEvent {
pub correct: Option<bool>,
}

impl Default for TestEvent {
fn default() -> Self {
Self {
time: Instant::now(),
key: KeyEvent::from(KeyCode::Null),
correct: None,
}
}
}

impl fmt::Debug for TestEvent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TestEvent")
Expand Down Expand Up @@ -53,9 +63,13 @@ impl Test {
}

pub fn handle_key(&mut self, key: KeyEvent) {
let word = self.words.get_mut(self.current_word).unwrap();

if key.modifiers.contains(KeyModifiers::CONTROL) && key.code == KeyCode::Char('h') {
if self.words[self.current_word].progress.is_empty() {
self.last_word();
}

let word = &mut self.words[self.current_word];

word.events.push(TestEvent {
time: Instant::now(),
correct: None,
Expand All @@ -65,15 +79,22 @@ impl Test {
return;
}

let word = &mut self.words[self.current_word];
match key.code {
KeyCode::Char(' ') | KeyCode::Enter => {
if !word.progress.is_empty() {
word.events.push(TestEvent {
time: Instant::now(),
correct: Some(word.text == word.progress),
key,
});
self.next_word();
}
}
KeyCode::Backspace => match word.progress.len() {
0 => self.last_word(),
_ => {
KeyCode::Backspace => {
if word.progress.is_empty() {
self.last_word();
} else {
word.events.push(TestEvent {
time: Instant::now(),
correct: Some(!word.text.starts_with(&word.progress[..])),
Expand Down
26 changes: 11 additions & 15 deletions src/test/results.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,9 @@ impl FromTermKey for Option<AsciiChar> {
}
}

impl From<&Test> for Results {
fn from(test: &Test) -> Self {
impl From<Test> for Results {
fn from(test: Test) -> Self {
let events = test.words.iter().flat_map(|w| w.events.iter());
Self {
cps: {
let mut cps = CpsData {
Expand All @@ -92,23 +93,20 @@ impl From<&Test> for Results {
per_key: [0f64; 256],
};

let mut events = test.words.iter().flat_map(|w| w.events.iter());

let mut last = events
.next()
.expect("The test was completed without any events.");
let mut key_count = [0usize; 256];
for event in events {
let event_cps = event

// NOTE: this should really be optimized to use less than O(n) space
let event_vec: Vec<&super::TestEvent> = events.clone().collect();
for win in event_vec.windows(2) {
let event_cps = win[1]
.time
.checked_duration_since(last.time)
.checked_duration_since(win[0].time)
.map(|d| d.as_secs_f64().recip());
last = &event;

if let Some(event_cps) = event_cps {
cps.per_event.push(event_cps);

if let Some(ac) = Option::<AsciiChar>::from_key(event.key) {
if let Some(ac) = Option::<AsciiChar>::from_key(win[1].key) {
cps.per_key[ac as usize] += event_cps;
key_count[ac as usize] += 1;
}
Expand All @@ -130,9 +128,7 @@ impl From<&Test> for Results {
per_key: [Fraction::default(); 256],
};

test.words
.iter()
.flat_map(|w| w.events.iter())
events
.filter(|event| event.correct.is_some())
.for_each(|event| {
if let Some(ch) = Option::<AsciiChar>::from_key(event.key) {
Expand Down

0 comments on commit 3a265bb

Please sign in to comment.