diff options
| -rw-r--r-- | README.md | 4 | ||||
| -rw-r--r-- | src/gui/message.rs | 7 | ||||
| -rw-r--r-- | src/gui/mod.rs | 9 | ||||
| -rw-r--r-- | src/gui/question.rs | 95 | 
4 files changed, 79 insertions, 36 deletions
| @@ -1,7 +1,7 @@  # Getting started  ```bash -nix develop --command bin/watch.sh +nix develop --command bin/dev-server.sh  ```  # Screenshot @@ -22,7 +22,7 @@ Cards are created from a plain text `./deck` file:  - good moorning : bonjour  - alternative 1 | alternative 2 : choix 1 | choix 2 -- cat (indication) : chat +- cat (indication) : chat [ʃa]  ```  # Backlog diff --git a/src/gui/message.rs b/src/gui/message.rs index f0d826a..61f57ba 100644 --- a/src/gui/message.rs +++ b/src/gui/message.rs @@ -1,6 +1,6 @@  use crate::gui::util; -use crossterm::event::{self, Event, KeyCode, KeyModifiers};  use anyhow::Result; +use crossterm::event::{self, Event, KeyCode, KeyModifiers};  use tui::{      backend::Backend,      layout::{Alignment, Constraint, Direction, Layout}, @@ -32,7 +32,10 @@ pub fn show<B: Backend>(          if wait {              if let Event::Key(key) = event::read()? { -                if key.code == KeyCode::Char('q') || key.code == KeyCode::Char('c') && key.modifiers.contains(KeyModifiers::CONTROL) { +                if key.code == KeyCode::Char('q') +                    || key.code == KeyCode::Char('c') +                        && key.modifiers.contains(KeyModifiers::CONTROL) +                {                      break;                  }              } diff --git a/src/gui/mod.rs b/src/gui/mod.rs index 1053de1..b500706 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -4,10 +4,10 @@ pub mod util;  use crate::{db, space_repetition, util::time};  use anyhow::Result; +use crossterm::terminal;  use rusqlite::Connection;  use std::io::Stdout;  use tui::{backend::CrosstermBackend, Terminal}; -use crossterm::{terminal};  pub type Term = Terminal<CrosstermBackend<Stdout>>; @@ -36,7 +36,12 @@ pub fn start(      loop {          let now = time::seconds_since_unix_epoch()?; -        let title = title(deck_name, answers, db::count_available(conn).unwrap_or(0), hide_progress); +        let title = title( +            deck_name, +            answers, +            db::count_available(conn).unwrap_or(0), +            hide_progress, +        );          match db::pick_random_ready(conn) {              Some(card) => match question::ask(term, &title, &card)? { diff --git a/src/gui/question.rs b/src/gui/question.rs index abb8fd7..1c44272 100644 --- a/src/gui/question.rs +++ b/src/gui/question.rs @@ -29,11 +29,7 @@ pub enum Response {      Answered { difficulty: Difficulty },  } -pub fn ask<B: Backend>( -    terminal: &mut Terminal<B>, -    title: &str, -    card: &Card, -) -> Result<Response> { +pub fn ask<B: Backend>(terminal: &mut Terminal<B>, title: &str, card: &Card) -> Result<Response> {      let mut state = State {          input: String::new(),          answer: Answer::Write, @@ -81,10 +77,11 @@ pub fn ask<B: Backend>(                  .style(match state.answer {                      Answer::Write => Style::default(),                      Answer::Difficulty { difficulty: _ } => { -                        if is_correct(&state.input, &card.responses) { -                            Style::default().fg(Color::Green) -                        } else { -                            Style::default().fg(Color::Red) +                        match check_response(&state.input, &card.responses) { +                            CheckResponse::Correct { phonetics: _ } => { +                                Style::default().fg(Color::Green) +                            } +                            CheckResponse::Incorrect => Style::default().fg(Color::Red),                          }                      }                  }) @@ -97,12 +94,17 @@ pub fn ask<B: Backend>(                  difficulty: selected,              } = state.answer              { -                if !is_correct(&state.input, &card.responses) { -                    let paragraph = Paragraph::new(util::center_vertically( -                        chunks[3], -                        &serialization::words_to_line(&card.responses), -                    )) -                    .alignment(Alignment::Center); +                let maybe_indication: Option<String> = +                    match check_response(&state.input, &card.responses) { +                        CheckResponse::Correct { phonetics } => phonetics, +                        CheckResponse::Incorrect => { +                            Some(serialization::words_to_line(&card.responses)) +                        } +                    }; + +                if let Some(indication) = maybe_indication { +                    let paragraph = Paragraph::new(util::center_vertically(chunks[3], &indication)) +                        .alignment(Alignment::Center);                      f.render_widget(paragraph, chunks[3]);                  }; @@ -138,10 +140,9 @@ pub fn ask<B: Backend>(              match state.answer {                  Answer::Write => match key.code {                      KeyCode::Enter => { -                        let difficulty = if is_correct(&state.input, &card.responses) { -                            Difficulty::Good -                        } else { -                            Difficulty::Again +                        let difficulty = match check_response(&state.input, &card.responses) { +                            CheckResponse::Correct { phonetics: _ } => Difficulty::Good, +                            CheckResponse::Incorrect => Difficulty::Again,                          };                          state.answer = Answer::Difficulty { difficulty }                      } @@ -150,7 +151,8 @@ pub fn ask<B: Backend>(                              if c == 'u' {                                  state.input.clear();                              } else if c == 'w' { -                                let mut words = state.input.split_whitespace().collect::<Vec<&str>>(); +                                let mut words = +                                    state.input.split_whitespace().collect::<Vec<&str>>();                                  if !words.is_empty() {                                      words.truncate(words.len() - 1);                                      let joined_words = words.join(" "); @@ -162,7 +164,9 @@ pub fn ask<B: Backend>(                              }                          } else {                              state.input.push(c); -                            if is_correct(&state.input, &card.responses) { +                            if let CheckResponse::Correct { phonetics: _ } = +                                check_response(&state.input, &card.responses) +                            {                                  state.answer = Answer::Difficulty {                                      difficulty: Difficulty::Good,                                  } @@ -205,19 +209,50 @@ pub fn ask<B: Backend>(      }  } -fn is_correct(input: &str, responses: &[String]) -> bool { -    // Remove whitespaces -    let input = input +enum CheckResponse { +    Incorrect, +    Correct { phonetics: Option<String> }, +} + +fn check_response(input: &str, responses: &[String]) -> CheckResponse { +    let input = remove_whitespaces(input); + +    responses +        .iter() +        .find(|r| remove_indications_and_phonetics(r) == input) +        .map(|r| CheckResponse::Correct { +            phonetics: extract_phonetics(r), +        }) +        .unwrap_or(CheckResponse::Incorrect) +} + +fn remove_whitespaces(input: &str) -> String { +    input          .split_whitespace()          .map(|word| word.trim())          .collect::<Vec<&str>>() -        .join(" "); +        .join(" ") +} -    responses -        .iter() -        .map(|r| r.split('(').collect::<Vec<&str>>()[0].trim()) -        .map(|r| r.split('[').collect::<Vec<&str>>()[0].trim()) -        .any(|x| x == input) +fn remove_indications_and_phonetics(response: &str) -> &str { +    response +        .split(|c| c == '(' || c == '[') +        .collect::<Vec<&str>>()[0] +        .trim() +} + +fn extract_phonetics(response: &str) -> Option<String> { +    let s1 = response.split('[').collect::<Vec<&str>>(); +    if s1.len() == 2 { +        let s2 = s1[1].split(']').collect::<Vec<&str>>(); +        if s2.len() > 1 { +            Some(format!("[{}]", s2[0])) +        } else { +            None +        } +    } else { +        None +    }  }  fn relative_element<T: Clone + PartialEq>(xs: &[T], x: &T, ri: i32) -> Option<T> { | 
