Skip to content

Commit

Permalink
fix(clafrica): update translation system
Browse files Browse the repository at this point in the history
Now, the user can browse through predicates and select the predicate to
commit.
  • Loading branch information
pythonbrad committed Aug 27, 2023
1 parent 80e36c8 commit 3946c3d
Show file tree
Hide file tree
Showing 9 changed files with 224 additions and 45 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
*~
*.swp
*.un
*.swo
2 changes: 2 additions & 0 deletions clafrica/data/config_sample.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
[core]
buffer_size = 12
auto_capitalize = false
auto_commit = false
page_size = 10

[data]
sample = { path = "./data_sample.toml" }
Expand Down
4 changes: 4 additions & 0 deletions clafrica/data/test.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ description = "Test code for testing purpose."
[core]
buffer_size = 64
auto_capitalize = true
auto_commit = true
page_size = 10

[data]
af = { value = "ɑ", alias = ["qf"] }
Expand All @@ -21,3 +23,5 @@ uuaf3 = { value = "ʉ̄ɑ̄", alias = ["uuqf\""] }

[translation]
hello = "hi"
heli = "helicopter"
hea = "health"
130 changes: 119 additions & 11 deletions clafrica/src/api.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,142 @@
pub trait Frontend {
fn update_screen(&mut self, _screen: (u64, u64)) {}
fn update_position(&mut self, _position: (f64, f64)) {}
fn update_text(&mut self, _text: &str) {}
fn add_predicate(&mut self, _remaining_code: &str, _text: &str) {}
fn set_input(&mut self, _text: &str) {}
fn set_page_size(&mut self, _size: usize) {}
fn add_predicate(&mut self, _code: &str, _remaining_code: &str, _text: &str) {}
fn display(&self) {}
fn clear_predicates(&mut self) {}
fn previous_predicate(&mut self) {}
fn next_predicate(&mut self) {}
fn get_selected_predicate(&self) -> Option<&(String, String, String)> {
Option::None
}
}

pub struct None;

impl Frontend for None {}

pub struct Console;
#[derive(Default)]
pub struct Console {
page_size: usize,
predicates: Vec<(String, String, String)>,
current_predicate_id: usize,
input: String,
}

impl Frontend for Console {
fn update_text(&mut self, text: &str) {
println!("text: {:?}", text);
fn set_page_size(&mut self, size: usize) {
self.page_size = size;
self.predicates = Vec::with_capacity(size);
}

fn set_input(&mut self, text: &str) {
self.input = text.to_owned();
}
fn add_predicate(&mut self, remaining_code: &str, text: &str) {
println!("predicate: {} ~{}", text, remaining_code);

fn display(&self) {
// Input
println!("input: {}", self.input);

// Predicates
let page_size = std::cmp::min(self.page_size, self.predicates.len());
println!(
"Predicates: {}",
self.predicates
.iter()
.enumerate()
.chain(self.predicates.iter().enumerate())
.skip(self.current_predicate_id)
.take(page_size)
.map(|(i, (_code, remaining_code, text))| format!(
"{}{}. {} ~{}\t ",
if i == self.current_predicate_id {
"*"
} else {
""
},
i + 1,
text,
remaining_code
))
.collect::<Vec<_>>()
.join("\t")
);
}

fn clear_predicates(&mut self) {
self.predicates.clear();
self.current_predicate_id = 0;
}

fn add_predicate(&mut self, code: &str, remaining_code: &str, text: &str) {
self.predicates
.push((code.to_owned(), remaining_code.to_owned(), text.to_owned()));
}

fn previous_predicate(&mut self) {
if self.predicates.is_empty() {
return;
};

self.current_predicate_id =
(self.current_predicate_id + self.predicates.len() - 1) % self.predicates.len();
self.display();
}

fn next_predicate(&mut self) {
if self.predicates.is_empty() {
return;
};

self.current_predicate_id = (self.current_predicate_id + 1) % self.predicates.len();
self.display();
}

fn get_selected_predicate(&self) -> Option<&(String, String, String)> {
self.predicates.get(self.current_predicate_id)
}
}

#[test]
fn test_console() {
let mut console = Console;
let mut none = None;
none.set_input("hello");
none.update_screen((64, 64));
none.update_position((64.0, 64.0));
none.set_input("input");
none.set_page_size(10);
none.add_predicate("hey", "y", "hello");
none.display();
none.clear_predicates();
none.previous_predicate();
none.next_predicate();
none.get_selected_predicate();

let mut console = Console::default();
console.set_page_size(10);
console.update_screen((0, 0));
console.update_position((0.0, 0.0));
console.update_text("hello");
console.add_predicate("_", "10/12/2003");
console.set_input("he");

console.add_predicate("hell", "llo", "hello");
console.add_predicate("helip", "lip", "helicopter");
console.add_predicate("heal", "al", "health");
console.display();
console.previous_predicate();
assert_eq!(
console.get_selected_predicate(),
Some(&("heal".to_owned(), "al".to_owned(), "health".to_owned()))
);
console.next_predicate();
assert_eq!(
console.get_selected_predicate(),
Some(&("hell".to_owned(), "llo".to_owned(), "hello".to_owned()))
);

console.clear_predicates();
none.update_text("hello");
console.previous_predicate();
console.next_predicate();
assert!(console.get_selected_predicate().is_none());
}
4 changes: 4 additions & 0 deletions clafrica/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ pub struct Config {
pub struct CoreConfig {
pub buffer_size: usize,
pub auto_capitalize: bool,
pub page_size: usize,
pub auto_commit: bool,
}

#[derive(Deserialize, Debug, Clone)]
Expand Down Expand Up @@ -163,6 +165,8 @@ mod tests {
let conf = Config::from_file(Path::new("./data/config_sample.toml")).unwrap();
assert_eq!(conf.core.as_ref().unwrap().buffer_size, 12);
assert!(!conf.core.as_ref().unwrap().auto_capitalize);
assert!(!conf.core.as_ref().unwrap().auto_commit);
assert_eq!(conf.core.as_ref().unwrap().page_size, 10);

let data = conf.extract_data();
assert_eq!(data.keys().len(), 19);
Expand Down
95 changes: 70 additions & 25 deletions clafrica/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,13 @@ pub fn run(
map,
config.core.as_ref().map(|e| e.buffer_size).unwrap_or(8),
);
let translator = Translator::new(config.extract_translation());
let translator = Translator::new(
config.extract_translation(),
config.core.as_ref().map(|e| e.auto_commit).unwrap_or(false),
);
let mut is_special_pressed = false;

frontend.set_page_size(config.core.as_ref().map(|e| e.page_size).unwrap_or(10));
frontend.update_screen(rdev::display_size().unwrap());

let (tx, rx) = mpsc::channel();
Expand Down Expand Up @@ -67,29 +72,50 @@ pub fn run(
});

for event in rx.iter() {
if let EventType::MouseMove { x, y } = &event.event_type {
frontend.update_position((*x, *y));
} else {
let (changed, committed) = processor.process(event);

if changed {
let input = processor.get_input();

frontend.clear_predicates();

if !committed {
translator.translate(&input).iter().for_each(
|(remaining_code, text, translated)| {
if *translated {
processor.commit(&input, text);
} else if !remaining_code.is_empty() {
frontend.add_predicate(remaining_code, text);
}
},
);
};

frontend.update_text(&input);
match event.event_type {
EventType::MouseMove { x, y } => {
frontend.update_position((x, y));
}
EventType::KeyPress(E_Key::ControlLeft | E_Key::ControlRight) => {
is_special_pressed = true;
}
EventType::KeyRelease(E_Key::ControlLeft | E_Key::ControlRight) => {
is_special_pressed = false;
}
EventType::KeyRelease(E_Key::Alt) if is_special_pressed => frontend.next_predicate(),
EventType::KeyRelease(E_Key::Unknown(151)) if is_special_pressed => {
frontend.previous_predicate()
}
EventType::KeyRelease(E_Key::Space) if is_special_pressed => {
if let Some(predicate) = frontend.get_selected_predicate() {
is_special_pressed = false;
processor.commit(&predicate.0, &predicate.1, &predicate.2);
}
}
_ if is_special_pressed => (),
_ => {
let (changed, committed) = processor.process(event);

if changed {
let input = processor.get_input();

frontend.clear_predicates();

if !committed {
translator.translate(&input).iter().for_each(
|(code, remaining_code, text, translated)| {
if *translated {
processor.commit(code, remaining_code, text);
} else if !text.is_empty() {
frontend.add_predicate(code, remaining_code, text);
}
},
);
};

frontend.set_input(&input);
frontend.display();
}
}
}
}
Expand Down Expand Up @@ -117,6 +143,8 @@ mod tests {

macro_rules! output {
( $textfield: expr, $expected: expr ) => {
thread::sleep(Duration::from_millis(500));

// A loop to be sure to got something stable
loop {
let a = $textfield.get_to_end((1, 0));
Expand All @@ -139,7 +167,7 @@ mod tests {
let test_config = Config::from_file(Path::new("./data/test.toml")).unwrap();

thread::spawn(move || {
run(test_config, api::Console).unwrap();
run(test_config, api::Console::default()).unwrap();
});
}

Expand Down Expand Up @@ -219,6 +247,23 @@ mod tests {
input!(KeyH KeyE KeyL KeyL KeyO, typing_speed_ms);
output!(textfield, format!("{LIMIT}hi"));

(0..2).for_each(|_| {
input!(Backspace, typing_speed_ms);
});

// We verify that the predicate selection work as expected
input!(KeyH KeyE, typing_speed_ms);
rdev::simulate(&KeyPress(ControlLeft)).unwrap();
input!(Unknown(151), typing_speed_ms);
input!(Alt, typing_speed_ms);
rdev::simulate(&KeyRelease(ControlLeft)).unwrap();

input!(KeyL KeyL, typing_speed_ms);
rdev::simulate(&KeyPress(ControlLeft)).unwrap();
input!(Space, typing_speed_ms);
rdev::simulate(&KeyRelease(ControlLeft)).unwrap();
output!(textfield, format!("{LIMIT}hi"));

rstk::end_wish();
}
}
2 changes: 1 addition & 1 deletion clafrica/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use clafrica::{api, prelude::Config, run};
use std::{env, path::Path, process};

fn main() {
let frontend = api::Console;
let frontend = api::Console::default();

let filename = env::args().nth(1).unwrap_or_else(|| {
eprintln!("Configuration file required");
Expand Down
10 changes: 8 additions & 2 deletions clafrica/src/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,17 @@ impl Processor {
(changed, committed)
}

pub fn commit(&mut self, code: &str, text: &str) {
pub fn commit(&mut self, code: &str, remaining_code: &str, text: &str) {
self.pause();
(0..code.len()).for_each(|_| self.keyboard.key_click(Key::Backspace));
rdev::simulate(&EventType::KeyRelease(E_Key::ControlLeft)).unwrap();
rdev::simulate(&EventType::KeyRelease(E_Key::ControlRight)).unwrap();

(0..code.len() - remaining_code.len())
.for_each(|_| self.keyboard.key_click(Key::Backspace));
self.keyboard.key_sequence(text);
self.resume();
// We clear the buffer
self.cursor.clear();
}

fn pause(&mut self) {
Expand Down
21 changes: 15 additions & 6 deletions clafrica/src/translator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,30 @@ use std::collections::HashMap;

pub struct Translator {
dictionary: HashMap<String, String>,
auto_commit: bool,
}

impl Translator {
pub fn new(dictionary: HashMap<String, String>) -> Self {
Self { dictionary }
pub fn new(dictionary: HashMap<String, String>, auto_commit: bool) -> Self {
Self {
dictionary,
auto_commit,
}
}

pub fn translate(&self, input: &str) -> Vec<(String, String, bool)> {
pub fn translate(&self, input: &str) -> Vec<(String, String, String, bool)> {
self.dictionary
.iter()
.filter_map(|(k, v)| {
if k == input {
Some((k.to_owned(), v.to_owned(), true))
} else if input.len() > 2 && k.starts_with(input) {
Some((k.chars().skip(input.len()).collect(), v.to_owned(), false))
Some((k.to_owned(), "".to_owned(), v.to_owned(), self.auto_commit))
} else if input.len() > 1 && k.starts_with(input) {
Some((
k.to_owned(),
k.chars().skip(input.len()).collect(),
v.to_owned(),
false,
))
} else {
None
}
Expand Down

0 comments on commit 3946c3d

Please sign in to comment.