extended letter map. Should be done now
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
mod keymaps;
|
||||
mod cml_functions;
|
||||
|
||||
use colored::*;
|
||||
use std::io::Write;
|
||||
use rust_translate::translate;
|
||||
use random_word::gen_len;
|
||||
use keymaps::get_keymap_rus;
|
||||
use cml_functions::*;
|
||||
|
||||
// TODO:
|
||||
// [ ] Line wrapping / character cap
|
||||
// [x] Working cursor navigation
|
||||
// [ ] Proper has_max_len (see below)
|
||||
// [x] Allow full caps translation (_YU / _Yu)
|
||||
// [x] Allow typing within text
|
||||
// [ ] Clean up result (whitespaces)
|
||||
//
|
||||
// BUG:
|
||||
// [x] Space -> Arrow left -> backspace -> panic
|
||||
// [x] 4x space -> backspace -> -> sp -> bs -> panic
|
||||
// [x] 2x space -> 2x backspace -> panic
|
||||
// [x] Cursor inserts, doesnt overwrite
|
||||
// [x] special letter -> left -> space -> cursor pos wrong
|
||||
|
||||
fn main() {
|
||||
let result = diff_string_copy("hellollo".to_string(), "hello".to_string());
|
||||
println!("\n{:?}", result);
|
||||
|
||||
|
||||
loop {
|
||||
let learned_word = learn_new_word();
|
||||
if learned_word.is_none() {
|
||||
break;
|
||||
};
|
||||
}
|
||||
//println!("Congrats!! You learned a new Word: {}", learned_word);
|
||||
}
|
||||
|
||||
|
||||
#[tokio::main]
|
||||
async fn learn_new_word() -> Option<String> {
|
||||
//print!("Welcome! Which word do you want to learn?\n> ");
|
||||
std::io::stdout().flush().unwrap();
|
||||
|
||||
//let mut vocab = String::new();
|
||||
//io::stdin().read_line(&mut vocab).expect("Failed to read line");
|
||||
//vocab = vocab.trim().to_string();
|
||||
|
||||
let vocab = gen_len(4, random_word::Lang::En).expect("no word found").to_string();
|
||||
|
||||
let vocab_tr = translate(&vocab, "en", "ru").await.unwrap();
|
||||
println!("\n{}: {}", vocab.red(), vocab_tr.green());
|
||||
|
||||
println!("Please repeat in russian 🇷🇺 (2x ESC to quit)");
|
||||
let input_opt = cml_input_line(get_keymap_rus());
|
||||
|
||||
input_opt.as_ref()?;
|
||||
|
||||
let input = input_opt.unwrap().to_string();
|
||||
//println!("] '{}'", input);
|
||||
|
||||
if input == vocab_tr {
|
||||
println!("Well done!");
|
||||
}
|
||||
else {
|
||||
println!("sorry, thats wrong!");
|
||||
if input.to_lowercase() == vocab_tr.to_lowercase() {
|
||||
println!("upper/lowecase counts!!");
|
||||
};
|
||||
}
|
||||
|
||||
Some(vocab.to_string())
|
||||
}
|
||||
|
||||
fn diff_string_copy(original: String, copy: String) -> Vec<u16> {
|
||||
let mut diff_indicies: Vec<u16> = Vec::new();
|
||||
let mut cp = copy.chars();
|
||||
|
||||
|
||||
for letter in original.chars() {
|
||||
|
||||
//print!("{}", letter);
|
||||
|
||||
#[allow(unused_assignments)]
|
||||
let mut cp_letter: Option<char> = None;
|
||||
|
||||
let mut found = false;
|
||||
|
||||
loop {
|
||||
cp_letter = cp.next();
|
||||
|
||||
if let Some(cp_lt_unpacked) = cp_letter {
|
||||
// print!("{}", letter);
|
||||
|
||||
if cp_lt_unpacked == letter {
|
||||
print!("{}", letter);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
// different letter
|
||||
print!("#");
|
||||
}
|
||||
}
|
||||
else {
|
||||
// cp iter has run out
|
||||
diff_indicies.push(1);
|
||||
println!("");
|
||||
return diff_indicies;
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
diff_indicies.push(1);
|
||||
}
|
||||
|
||||
}
|
||||
println!("\nended");
|
||||
diff_indicies
|
||||
}
|
||||
|
||||
Generated
+1739
File diff suppressed because it is too large
Load Diff
@@ -4,4 +4,8 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
colored = "2.1.0"
|
||||
random_word = { version = "0.4.3", features = ["en"] }
|
||||
rust-translate = "0.1.3"
|
||||
termion = "4.0.3"
|
||||
tokio = "1.41.1"
|
||||
|
||||
+138
-4
@@ -1,8 +1,9 @@
|
||||
use std::{char, io::{self, Write}};
|
||||
use std::{char, io::{self, Read, Write}};
|
||||
use termion::raw::IntoRawMode;
|
||||
|
||||
pub fn cml_type(text: String, input: &mut String, cursor_x: &mut usize) {
|
||||
// ON DATA
|
||||
//input.push_str(&text);
|
||||
//input.push_str(&text);
|
||||
let inp = input.clone();
|
||||
let inp_vec: Vec<char> = inp.chars().collect(); // TODO: make this char based
|
||||
|
||||
@@ -56,9 +57,9 @@ pub fn cml_move_cursor(direction: char, input: &mut str, cursor_x: &mut usize) {
|
||||
}
|
||||
|
||||
pub fn cml_delete_str_within(text: String, input: &mut String, cursor_x: &mut usize) {
|
||||
for _ in 0..text.len() {
|
||||
for _ in 0..text.len() {
|
||||
cml_delete_within(input, cursor_x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cml_delete_within(input: &mut String, cursor_x: &mut usize) {
|
||||
@@ -91,3 +92,136 @@ pub fn cml_delete_within(input: &mut String, cursor_x: &mut usize) {
|
||||
*input = new_inp_vec.into_iter().collect::<String>();
|
||||
}
|
||||
|
||||
|
||||
pub fn cml_input_line(keymap: Vec<(String, String)>) -> Option<String> {
|
||||
let block: char = '_';
|
||||
|
||||
let _stdout = io::stdout().into_raw_mode();
|
||||
let stdin = io::stdin().lock();
|
||||
let mut input = String::new();
|
||||
let mut cursor_x = 0;
|
||||
|
||||
let mut cycles_to_skip: usize = 0;
|
||||
|
||||
print!("] ");
|
||||
io::stdout().flush().unwrap();
|
||||
|
||||
for byte in stdin.bytes() {
|
||||
let inp = byte.unwrap();
|
||||
let inp_char = inp as char;
|
||||
|
||||
// HANDLE OTHER BYTES OF MULTY BYTE ANSI ESC CODES
|
||||
|
||||
if cycles_to_skip == 2 {
|
||||
if inp == b'[' {
|
||||
// arrow key
|
||||
cycles_to_skip -= 1;
|
||||
continue;
|
||||
}
|
||||
else if inp == 27 {
|
||||
// pressed escape key twice
|
||||
return None;
|
||||
}
|
||||
else {
|
||||
cycles_to_skip = 0;
|
||||
}
|
||||
}
|
||||
else if cycles_to_skip == 1 {
|
||||
cycles_to_skip = 0;
|
||||
|
||||
match inp_char {
|
||||
'D'|'C' => {
|
||||
// Left/Right arrow
|
||||
cml_move_cursor(inp_char, &mut input, &mut cursor_x);
|
||||
},
|
||||
_ => { }
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// HANDLE FIRST BYTE / SINGLE BYTE
|
||||
|
||||
match inp {
|
||||
27 => { // ansi escape sequence
|
||||
// ansi codes begin with 27 and then continue with a value pair.
|
||||
// in total we need three cyclesto recieve the whole code and determine
|
||||
// if we want to print (left/right arrow) or skip.
|
||||
// Example: \o33 [ D <- three bytes, last byte tells direction, first = 27
|
||||
cycles_to_skip = 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
127 => { // Backspace
|
||||
cml_delete_within(&mut input, &mut cursor_x);
|
||||
},
|
||||
|
||||
b'\r' => { // Return
|
||||
print!("\r");
|
||||
println!();
|
||||
break; // end input on return key
|
||||
},
|
||||
|
||||
b'\t' => { // not tab allowed }:<
|
||||
continue;
|
||||
},
|
||||
|
||||
_ => { // characters
|
||||
if !(inp_char.is_ascii() && (inp_char.is_alphanumeric() || ['.', ',', '!', '?', '[', ' ', '_', '\''].contains(&inp_char))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cml_type(inp_char.to_string(), &mut input, &mut cursor_x);
|
||||
|
||||
for value in keymap.iter() {
|
||||
|
||||
let input_vec: Vec<char> = input.chars().collect();
|
||||
let (left_vec, _right_vec) = input_vec.split_at(cursor_x);
|
||||
|
||||
let left_str = left_vec.iter().collect::<String>();
|
||||
//let right_str = right_vec.iter().collect::<String>();
|
||||
// left_str at this point contains the typed char but no translations
|
||||
|
||||
if left_str.ends_with(&value.0) {
|
||||
// we just typed a translatable letter
|
||||
//println!("\n|{:?}-{:?}|\n", left_str, right_str);
|
||||
|
||||
let x: i16 = (left_str.chars().count() - value.0.chars().count()) as i16 - 1;
|
||||
if x >= 0 {
|
||||
// valid leter of input
|
||||
// test if its a break char
|
||||
let char_before = left_str.chars().nth(x as usize).unwrap();
|
||||
if char_before == block {
|
||||
// break char was placed
|
||||
if value.0.chars().count() == 1 { // TODO: HERE: proper has_max_len fn for "shsh"
|
||||
// has not reached max lenght
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
// blocker was used
|
||||
// delete one char to account for the blocker. With the
|
||||
// deletion further down this will wipe the entire thing and
|
||||
// only leave the new character!
|
||||
cml_delete_within(&mut input, &mut cursor_x);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// is already first letter
|
||||
//print!("|invalid| ");
|
||||
}
|
||||
|
||||
cml_delete_str_within(value.clone().0, &mut input, &mut cursor_x);
|
||||
cml_type(value.clone().1, &mut input, &mut cursor_x);
|
||||
break; // exit so if zh and z both work the first entry (zh) is
|
||||
// prioritized. Works for now if list is properly sorted.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
io::stdout().flush().unwrap();
|
||||
}
|
||||
|
||||
//println!("\nCursor ended at pos: {}\r", cursor_x);
|
||||
Some(input)
|
||||
// stdout is out of scope and raw mode is ended automatically!
|
||||
}
|
||||
|
||||
@@ -26,10 +26,29 @@ pub fn get_keymap_rus() -> Vec<(String,String)> {
|
||||
(format!("{}Yo{}", u, p), "Ё".to_string()),
|
||||
(format!("{}YO{}", u, p), "Ё".to_string()),
|
||||
|
||||
(format!("{}ya{}", l, p), "я".to_string()),
|
||||
(format!("{}Ya{}", u, p), "Я".to_string()),
|
||||
(format!("{}YA{}", u, p), "Я".to_string()),
|
||||
|
||||
(format!("{}iy{}", l, p), "ы".to_string()),
|
||||
(format!("{}Iy{}", u, p), "Ы".to_string()),
|
||||
(format!("{}IY{}", u, p), "Ы".to_string()),
|
||||
|
||||
(format!("{}sh{}", l, p), "ш".to_string()),
|
||||
(format!("{}Sh{}", u, p), "Ш".to_string()),
|
||||
(format!("{}SH{}", u, p), "Ш".to_string()),
|
||||
|
||||
(format!("{}kh{}", l, p), "х".to_string()),
|
||||
(format!("{}Kh{}", u, p), "Х".to_string()),
|
||||
(format!("{}KH{}", u, p), "Х".to_string()),
|
||||
|
||||
(format!("{}ts{}", l, p), "ц".to_string()),
|
||||
(format!("{}Ts{}", u, p), "Ц".to_string()),
|
||||
(format!("{}TS{}", u, p), "Ц".to_string()),
|
||||
|
||||
(format!("{}_i{}", l, p), "й".to_string()),
|
||||
(format!("{}_I{}", u, p), "Й".to_string()),
|
||||
|
||||
(format!("{}k{}", l, p), "к".to_string()),
|
||||
(format!("{}K{}", u, p), "К".to_string()),
|
||||
|
||||
@@ -57,6 +76,9 @@ pub fn get_keymap_rus() -> Vec<(String,String)> {
|
||||
(format!("{}i{}", l, p), "и".to_string()),
|
||||
(format!("{}I{}", u, p), "И".to_string()),
|
||||
|
||||
(format!("{}x{}", l, p), "х".to_string()),
|
||||
(format!("{}X{}", u, p), "Х".to_string()),
|
||||
|
||||
(format!("{}z{}", l, p), "з".to_string()),
|
||||
(format!("{}Z{}", u, p), "З".to_string()),
|
||||
|
||||
@@ -65,5 +87,31 @@ pub fn get_keymap_rus() -> Vec<(String,String)> {
|
||||
|
||||
(format!("{}t{}", l, p), "т".to_string()),
|
||||
(format!("{}T{}", u, p), "Т".to_string()),
|
||||
|
||||
(format!("{}a{}", l, p), "а".to_string()),
|
||||
(format!("{}A{}", u, p), "А".to_string()),
|
||||
|
||||
(format!("{}p{}", l, p), "п".to_string()),
|
||||
(format!("{}P{}", u, p), "П".to_string()),
|
||||
|
||||
(format!("{}n{}", l, p), "н".to_string()),
|
||||
(format!("{}N{}", u, p), "Н".to_string()),
|
||||
|
||||
(format!("{}s{}", l, p), "с".to_string()),
|
||||
(format!("{}S{}", u, p), "С".to_string()),
|
||||
|
||||
(format!("{}e{}", l, p), "э".to_string()),
|
||||
(format!("{}E{}", u, p), "Э".to_string()),
|
||||
|
||||
(format!("{}u{}", l, p), "у".to_string()),
|
||||
(format!("{}U{}", u, p), "Y".to_string()),
|
||||
|
||||
(format!("{}b{}", l, p), "б".to_string()),
|
||||
(format!("{}B{}", u, p), "Б".to_string()),
|
||||
|
||||
(format!("{}y{}", l, p), "ы".to_string()),
|
||||
(format!("{}Y{}", u, p), "Ы".to_string()),
|
||||
|
||||
(format!("{}'{}", l, p), "ь".to_string()),
|
||||
)
|
||||
}
|
||||
|
||||
+97
-128
@@ -1,9 +1,10 @@
|
||||
mod keymaps;
|
||||
mod cml_functions;
|
||||
|
||||
use std::{char, io::{self, Read, Write}};
|
||||
use termion::raw::IntoRawMode;
|
||||
|
||||
use colored::*;
|
||||
use std::io::Write;
|
||||
use rust_translate::translate;
|
||||
use random_word::gen_len;
|
||||
use keymaps::get_keymap_rus;
|
||||
use cml_functions::*;
|
||||
|
||||
@@ -13,6 +14,7 @@ use cml_functions::*;
|
||||
// [ ] Proper has_max_len (see below)
|
||||
// [x] Allow full caps translation (_YU / _Yu)
|
||||
// [x] Allow typing within text
|
||||
// [ ] Clean up result (whitespaces)
|
||||
//
|
||||
// BUG:
|
||||
// [x] Space -> Arrow left -> backspace -> panic
|
||||
@@ -22,132 +24,99 @@ use cml_functions::*;
|
||||
// [x] special letter -> left -> space -> cursor pos wrong
|
||||
|
||||
fn main() {
|
||||
println!("Please enter the following in russian: diktat");
|
||||
let input = input_line(get_keymap_rus());
|
||||
println!("] '{}'", input);
|
||||
// TODO: trim whitespace
|
||||
}
|
||||
let result = diff_string_copy("hellollo".to_string(), "hello".to_string());
|
||||
println!("\n{:?}", result);
|
||||
|
||||
fn input_line(keymap: Vec<(String, String)>) -> String {
|
||||
let block: char = '_';
|
||||
|
||||
let _stdout = io::stdout().into_raw_mode();
|
||||
let stdin = io::stdin().lock();
|
||||
let mut input = String::new();
|
||||
let mut cursor_x = 0;
|
||||
|
||||
let mut cycles_to_skip: usize = 0;
|
||||
|
||||
print!("] ");
|
||||
io::stdout().flush().unwrap();
|
||||
|
||||
for byte in stdin.bytes() {
|
||||
let inp = byte.unwrap();
|
||||
let inp_char = inp as char;
|
||||
|
||||
// HANDLE OTHER BYTES OF MULTY BYTE ANSI ESC CODES
|
||||
|
||||
if cycles_to_skip == 2 {
|
||||
cycles_to_skip -= 1;
|
||||
continue; // here ansi escape chars are usually '[', we can just skip
|
||||
}
|
||||
else if cycles_to_skip == 1 {
|
||||
cycles_to_skip = 0;
|
||||
|
||||
match inp_char {
|
||||
'D'|'C' => {
|
||||
// Left/Right arrow
|
||||
cml_move_cursor(inp_char, &mut input, &mut cursor_x);
|
||||
},
|
||||
_ => { }
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// HANDLE FIRST BYTE / SINGLE BYTE
|
||||
|
||||
match inp {
|
||||
27 => { // ansi escape sequence
|
||||
// ansi codes begin with 27 and then continue with a value pair.
|
||||
// in total we need three cyclesto recieve the whole code and determine
|
||||
// if we want to print (left/right arrow) or skip.
|
||||
// Example: \o33 [ D <- three bytes, last byte tells direction, first = 27
|
||||
cycles_to_skip = 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
127 => { // Backspace
|
||||
cml_delete_within(&mut input, &mut cursor_x);
|
||||
},
|
||||
|
||||
b'\r' => { // Return
|
||||
print!("\r");
|
||||
println!();
|
||||
break; // end input on return key
|
||||
},
|
||||
|
||||
b'\t' => { // not tab allowed }:<
|
||||
continue;
|
||||
},
|
||||
|
||||
_ => { // characters
|
||||
if !(inp_char.is_ascii() && (inp_char.is_alphanumeric() || ['.', ',', '!', '?', '[', ' ', '_'].contains(&inp_char))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cml_type(inp_char.to_string(), &mut input, &mut cursor_x);
|
||||
|
||||
for value in keymap.iter() {
|
||||
|
||||
let input_vec: Vec<char> = input.chars().collect();
|
||||
let (left_vec, _right_vec) = input_vec.split_at(cursor_x);
|
||||
|
||||
let left_str = left_vec.iter().collect::<String>();
|
||||
//let right_str = right_vec.iter().collect::<String>();
|
||||
// left_str at this point contains the typed char but no translations
|
||||
|
||||
if left_str.ends_with(&value.0) {
|
||||
// we just typed a translatable letter
|
||||
//println!("\n|{:?}-{:?}|\n", left_str, right_str);
|
||||
|
||||
let x: i16 = (left_str.chars().count() - value.0.chars().count()) as i16 - 1;
|
||||
if x >= 0 {
|
||||
// valid leter of input
|
||||
// test if its a break char
|
||||
let char_before = left_str.chars().nth(x as usize).unwrap();
|
||||
if char_before == block {
|
||||
// break char was placed
|
||||
if value.0.chars().count() == 1 { // TODO: HERE: proper has_max_len fn for "shsh"
|
||||
// has not reached max lenght
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
// blocker was used
|
||||
// delete one char to account for the blocker. With the
|
||||
// deletion further down this will wipe the entire thing and
|
||||
// only leave the new character!
|
||||
cml_delete_within(&mut input, &mut cursor_x);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// is already first letter
|
||||
//print!("|invalid| ");
|
||||
}
|
||||
|
||||
cml_delete_str_within(value.clone().0, &mut input, &mut cursor_x);
|
||||
cml_type(value.clone().1, &mut input, &mut cursor_x);
|
||||
break; // exit so if zh and z both work the first entry (zh) is
|
||||
// prioritized. Works for now if list is properly sorted.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
io::stdout().flush().unwrap();
|
||||
loop {
|
||||
let learned_word = learn_new_word();
|
||||
if learned_word.is_none() {
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
//println!("\nCursor ended at pos: {}\r", cursor_x);
|
||||
input
|
||||
// stdout is out of scope and raw mode is ended automatically!
|
||||
//println!("Congrats!! You learned a new Word: {}", learned_word);
|
||||
}
|
||||
|
||||
|
||||
#[tokio::main]
|
||||
async fn learn_new_word() -> Option<String> {
|
||||
//print!("Welcome! Which word do you want to learn?\n> ");
|
||||
std::io::stdout().flush().unwrap();
|
||||
|
||||
//let mut vocab = String::new();
|
||||
//io::stdin().read_line(&mut vocab).expect("Failed to read line");
|
||||
//vocab = vocab.trim().to_string();
|
||||
|
||||
let vocab = gen_len(4, random_word::Lang::En).expect("no word found").to_string();
|
||||
|
||||
let vocab_tr = translate(&vocab, "en", "ru").await.unwrap();
|
||||
println!("\n{}: {}", vocab.red(), vocab_tr.green());
|
||||
|
||||
println!("Please repeat in russian 🇷🇺 (2x ESC to quit)");
|
||||
let input_opt = cml_input_line(get_keymap_rus());
|
||||
|
||||
input_opt.as_ref()?;
|
||||
|
||||
let input = input_opt.unwrap().to_string();
|
||||
//println!("] '{}'", input);
|
||||
|
||||
if input == vocab_tr {
|
||||
println!("Well done!");
|
||||
}
|
||||
else {
|
||||
println!("sorry, thats wrong!");
|
||||
if input.to_lowercase() == vocab_tr.to_lowercase() {
|
||||
println!("upper/lowecase counts!!");
|
||||
};
|
||||
}
|
||||
|
||||
Some(vocab.to_string())
|
||||
}
|
||||
|
||||
fn diff_string_copy(original: String, copy: String) -> Vec<u16> {
|
||||
let mut diff_indicies: Vec<u16> = Vec::new();
|
||||
let mut cp = copy.chars();
|
||||
|
||||
|
||||
for letter in original.chars() {
|
||||
|
||||
//print!("{}", letter);
|
||||
|
||||
#[allow(unused_assignments)]
|
||||
let mut cp_letter: Option<char> = None;
|
||||
|
||||
let mut found = false;
|
||||
|
||||
loop {
|
||||
cp_letter = cp.next();
|
||||
|
||||
if let Some(cp_lt_unpacked) = cp_letter {
|
||||
// print!("{}", letter);
|
||||
|
||||
if cp_lt_unpacked == letter {
|
||||
print!("{}", letter);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
// different letter
|
||||
print!("#");
|
||||
}
|
||||
}
|
||||
else {
|
||||
// cp iter has run out
|
||||
diff_indicies.push(1);
|
||||
println!("");
|
||||
return diff_indicies;
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
diff_indicies.push(1);
|
||||
}
|
||||
|
||||
}
|
||||
println!("\nended");
|
||||
diff_indicies
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user