Skip to content

Commit

Permalink
feat(translator): Improve auto-suggestion
Browse files Browse the repository at this point in the history
  • Loading branch information
pythonbrad committed Oct 19, 2023
1 parent bcd189e commit d24534c
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 56 deletions.
4 changes: 4 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions config/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ authors = ["Brady Fomegne <[email protected]>"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
indexmap = { version = "2.0.2", features = ["serde"] }
rhai = "1.16.2"
serde = { version = "1.0.188", features = ["derive"] }
toml = "0.8.2"
27 changes: 14 additions & 13 deletions config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,21 @@
#![deny(missing_docs)]

use indexmap::IndexMap;
use rhai::{Engine, AST};
use serde::Deserialize;
use std::result::Result;
use std::{collections::HashMap, error, fs, path::Path};
use std::{error, fs, path::Path};
use toml::{self};

/// Hold information about a configuration.
#[derive(Deserialize, Debug, Clone)]
pub struct Config {
/// The core config.
pub core: Option<CoreConfig>,
data: Option<HashMap<String, Data>>,
translators: Option<HashMap<String, Data>>,
translation: Option<HashMap<String, Data>>,
data: Option<IndexMap<String, Data>>,
translators: Option<IndexMap<String, Data>>,
translation: Option<IndexMap<String, Data>>,
}

/// Core information about a configuration.
Expand Down Expand Up @@ -87,7 +88,7 @@ impl Config {
.unwrap_or(true);

// Data
let mut data = HashMap::new();
let mut data = IndexMap::new();

config.data.unwrap_or_default().iter().try_for_each(
|(key, value)| -> Result<(), Box<dyn error::Error>> {
Expand All @@ -113,7 +114,7 @@ impl Config {
config.data = Some(data);

// Translators
let mut translators = HashMap::new();
let mut translators = IndexMap::new();

config.translators.unwrap_or_default().iter().try_for_each(
|(key, value)| -> Result<(), Box<dyn error::Error>> {
Expand All @@ -135,7 +136,7 @@ impl Config {
config.translators = Some(translators);

// Translation
let mut translation = HashMap::new();
let mut translation = IndexMap::new();

config.translation.unwrap_or_default().iter().try_for_each(
|(key, value)| -> Result<(), Box<dyn error::Error>> {
Expand Down Expand Up @@ -169,8 +170,8 @@ impl Config {
}

/// Extract the data from the configuration.
pub fn extract_data(&self) -> HashMap<String, String> {
let empty = HashMap::default();
pub fn extract_data(&self) -> IndexMap<String, String> {
let empty = IndexMap::default();

self.data
.as_ref()
Expand All @@ -187,8 +188,8 @@ impl Config {
}

/// Extract the translators from the configuration.
pub fn extract_translators(&self) -> Result<HashMap<String, AST>, Box<dyn error::Error>> {
let empty = HashMap::default();
pub fn extract_translators(&self) -> Result<IndexMap<String, AST>, Box<dyn error::Error>> {
let empty = IndexMap::default();
let mut engine = Engine::new();

// allow nesting up to 50 layers of expressions/statements
Expand Down Expand Up @@ -220,8 +221,8 @@ impl Config {
}

/// Extract the translation from the configuration.
pub fn extract_translation(&self) -> HashMap<String, Vec<String>> {
let empty = HashMap::new();
pub fn extract_translation(&self) -> IndexMap<String, Vec<String>> {
let empty = IndexMap::new();

self.translation
.as_ref()
Expand Down
2 changes: 2 additions & 0 deletions engine/translator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ authors = ["Brady Fomegne <[email protected]>"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
indexmap = { version = "2.0.2", features = ["serde"] }
rhai = "1.16.2"
strsim = "0.10.0"
128 changes: 93 additions & 35 deletions engine/translator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,58 +3,89 @@
//! Example
//! ```rust
//! use clafrica_translator::{Engine, Translator};
//! use std::collections::HashMap;
//! use indexmap::IndexMap;
//!
//! // Translation via scripting
//! let engine = Engine::new();
//! let hi = engine.compile(r#"
//! let jump = engine.compile(r#"
//! fn translate(input) {
//! if input == "hi" {
//! ["hi", "", "hello", true]
//! if input == "jump" {
//! [input, "", "\n", false]
//! }
//! }
//! "#).unwrap();
//! let mut translators = HashMap::new();
//! translators.insert("hi".to_string(), hi);
//! let mut translators = IndexMap::new();
//! translators.insert("jump".to_string(), jump);
//!
//! // Translation via dictionary
//! let mut dictionary = HashMap::new();
//! dictionary.insert("halo".to_string(), ["hello".to_string()].to_vec());
//! let mut dictionary = IndexMap::new();
//! dictionary.insert("jump".to_string(), ["sauter".to_string()].to_vec());
//! dictionary.insert("jumper".to_string(), ["sauteur".to_string()].to_vec());
//! dictionary.insert("nihao".to_string(), ["hello".to_string()].to_vec());
//!
//! // We build the translator.
//! let translator = Translator::new(dictionary, translators, true);
//!
//! assert_eq!(
//! translator.translate("hi"),
//! translator.translate("jump"),
//! vec![
//! (
//! "jump".to_owned(),
//! "".to_owned(),
//! vec!["sauter".to_owned()],
//! true
//! ),
//! // Programmable translation
//! (
//! "jump".to_owned(),
//! "".to_owned(),
//! vec!["\n".to_owned()],
//! false
//! ),
//! // Auto-completion
//! (
//! "jumper".to_owned(),
//! "er".to_owned(),
//! vec!["sauteur".to_owned()],
//! false
//! )
//! ]
//! );
//!
//! // Auto-suggestion / Auto-correction
//! assert_eq!(
//! translator.translate("junp"),
//! vec![(
//! "hi".to_owned(),
//! "jump".to_owned(),
//! "".to_owned(),
//! vec!["hello".to_owned()],
//! true
//! vec!["sauter".to_owned()],
//! false
//! )]
//! );
//!
//! ```
//!
#![deny(missing_docs)]

use indexmap::IndexMap;
pub use rhai::Engine;
use rhai::{Array, Scope, AST};
use std::collections::HashMap;
use std::cmp::Ordering;
use strsim::{self};

/// Core structure of the translator.
pub struct Translator {
dictionary: HashMap<String, Vec<String>>,
translators: HashMap<String, AST>,
dictionary: IndexMap<String, Vec<String>>,
translators: IndexMap<String, AST>,
auto_commit: bool,
}

impl Translator {
/// Initiate a new translator.
pub fn new(
dictionary: HashMap<String, Vec<String>>,
translators: HashMap<String, AST>,
dictionary: IndexMap<String, Vec<String>>,
translators: IndexMap<String, AST>,
auto_commit: bool,
) -> Self {
Self {
Expand All @@ -68,23 +99,42 @@ impl Translator {
pub fn translate(&self, input: &str) -> Vec<(String, String, Vec<String>, bool)> {
let mut scope = Scope::new();
let engine = Engine::new();

self.dictionary
let mut predicates: Vec<(f64, (String, String, Vec<String>, bool))> = self
.dictionary
.iter()
.filter_map(|(key, value)| {
if key == input {
if input.len() < 2 || input.len() > key.len() {
None
} else if key == input {
Some((
key.to_owned(),
"".to_owned(),
value.to_owned(),
self.auto_commit,
1.0,
(
key.to_owned(),
"".to_owned(),
value.to_owned(),
self.auto_commit,
),
))
} else if input.len() > 1 && key.starts_with(input) {
} else if key.len() == input.len() {
let confidence = strsim::hamming(key.as_ref(), input)
.map(|n| 1.0 - (n as f64 / key.len() as f64))
.unwrap_or(0.0);

(confidence > 0.7).then(|| {
(
confidence,
(key.to_owned(), "".to_owned(), value.to_owned(), false),
)
})
} else if key.starts_with(input) {
Some((
key.to_owned(),
key.chars().skip(input.len()).collect(),
value.to_owned(),
false,
0.5,
(
key.to_owned(),
key.chars().skip(input.len()).collect(),
value.to_owned(),
false,
),
))
} else {
None
Expand All @@ -107,9 +157,17 @@ impl Translator {
.collect();
let translated = data[3].clone().as_bool().unwrap();

(code, remaining_code, texts, translated)
(1.0, (code, remaining_code, texts, translated))
})
}))
.collect();

// from the best to the worst
predicates.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(Ordering::Equal));

predicates
.into_iter()
.map(|(_, predicate)| predicate)
.collect()
}
}
Expand All @@ -119,7 +177,7 @@ mod tests {
#[test]
fn test_translate() {
use crate::{Engine, Translator};
use std::collections::HashMap;
use indexmap::IndexMap;

let engine = Engine::new();
let ast1 = engine.compile("fn translate(input) {}").unwrap();
Expand All @@ -134,11 +192,11 @@ mod tests {
"#,
)
.unwrap();
let mut translators = HashMap::new();
let mut translators = IndexMap::new();
translators.insert("none".to_string(), ast1);
translators.insert("some".to_string(), ast2);

let mut dictionary = HashMap::new();
let mut dictionary = IndexMap::new();
dictionary.insert("halo".to_string(), ["hello".to_string()].to_vec());

let translator = Translator::new(dictionary, translators, true);
Expand All @@ -163,12 +221,12 @@ mod tests {
)]
);
assert_eq!(
translator.translate("halo"),
translator.translate("helo"),
vec![(
"halo".to_owned(),
"".to_owned(),
vec!["hello".to_owned()],
true
false
)]
);
}
Expand Down
2 changes: 1 addition & 1 deletion service/data/test.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ hi = "./scripts/hi.rhai"
[translation]
hello = "hi"
heli = "helicopter"
hea = "health"
heal = { value = "health", alias = ["heql"] }
vuue = "vʉe"
16 changes: 9 additions & 7 deletions service/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,17 +110,19 @@ pub fn run(config: Config, mut frontend: impl Frontend) -> Result<(), Box<dyn er

frontend.clear_predicates();

translator.translate(&input).iter().for_each(
|(code, remaining_code, texts, translated)| {
translator
.translate(&input)
.iter()
.take(page_size * 2)
.for_each(|(code, remaining_code, texts, translated)| {
texts.iter().for_each(|text| {
if auto_commit && *translated {
preprocessor.commit(text);
} else if !text.is_empty() {
frontend.add_predicate(code, remaining_code, text);
}
});
},
);
});

frontend.set_input(&input);
frontend.display();
Expand Down Expand Up @@ -289,16 +291,16 @@ mod tests {
input!(ShiftRight, typing_speed_ms);
rdev::simulate(&KeyRelease(ControlLeft)).unwrap();

input!(KeyL KeyL, typing_speed_ms);
input!(KeyA, typing_speed_ms);
rdev::simulate(&KeyPress(ControlLeft)).unwrap();
input!(ControlRight, typing_speed_ms);
rdev::simulate(&KeyRelease(ControlLeft)).unwrap();
output!(textfield, format!("{LIMIT}uuɑαⱭⱭɑɑhihellohi"));
output!(textfield, format!("{LIMIT}uuɑαⱭⱭɑɑhihellohealth"));
input!(Escape, typing_speed_ms);

// We verify that we don't have a conflict
// between the translator and the processor
input!(KeyV KeyU KeyU KeyE, typing_speed_ms);
output!(textfield, format!("{LIMIT}uuɑαⱭⱭɑɑhihellohivʉe"));
output!(textfield, format!("{LIMIT}uuɑαⱭⱭɑɑhihellohealthvʉe"));
}
}

0 comments on commit d24534c

Please sign in to comment.