extended letter map. Should be done now

This commit is contained in:
magnusKue
2024-11-25 20:10:47 +01:00
parent 256e6d4043
commit 281d688f0e
6 changed files with 2148 additions and 132 deletions
+122
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+4
View File
@@ -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
View File
@@ -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!
}
+48
View File
@@ -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
View File
@@ -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
}