diff --git a/build.rs b/build.rs index 9def218..9e00bee 100644 --- a/build.rs +++ b/build.rs @@ -1,12 +1,12 @@ -use itertools::Itertools; -use std::collections::HashSet; use std::env; use std::fs::File; use std::io::{BufWriter, Write}; use std::path::Path; -/// Should build.rs generate the autocomplete hashmap to codegen.rs? -const DO_HASHMAP_GEN: bool = false; +const SUPPORTED_FUNCTIONS: [&str; 22] = [ + "abs", "signum", "sin", "cos", "tan", "asin", "acos", "atan", "sinh", "cosh", "tanh", "floor", + "round", "ceil", "trunc", "fract", "exp", "sqrt", "cbrt", "ln", "log2", "log10", +]; fn main() { // rebuild if new commit or contents of `assets` folder changed @@ -18,96 +18,36 @@ fn main() { .run(); shadow_rs::new().unwrap(); - if DO_HASHMAP_GEN { - generate_hashmap(); - } + generate_hashmap(); } fn generate_hashmap() { let path = Path::new(&env::var("OUT_DIR").unwrap()).join("codegen.rs"); let mut file = BufWriter::new(File::create(&path).unwrap()); + let string_hashmap = compile_hashmap( + SUPPORTED_FUNCTIONS + .to_vec() + .iter() + .map(|a| a.to_string()) + .collect(), + ); + + let mut hashmap = phf_codegen::Map::new(); + + for (key, value) in string_hashmap.iter() { + hashmap.entry(key, value); + } write!( &mut file, "static COMPLETION_HASHMAP: phf::Map<&'static str, HintEnum> = {}", - compile_hashmap().build() + hashmap.build() ) .unwrap(); writeln!(&mut file, ";").unwrap(); } -/// List of supported functions from exmex -const SUPPORTED_FUNCTIONS: [&str; 22] = [ - "abs", "signum", "sin", "cos", "tan", "asin", "acos", "atan", "sinh", "cosh", "tanh", "floor", - "round", "ceil", "trunc", "fract", "exp", "sqrt", "cbrt", "ln", "log2", "log10", -]; - -const QUOTE: char = '"'; - -fn compile_hashmap() -> phf_codegen::Map { - let functions_processed: Vec = SUPPORTED_FUNCTIONS - .iter() - .map(|e| e.to_string() + "(") - .collect(); - - let mut seen = HashSet::new(); - - let powerset = functions_processed - .into_iter() - .map(|func| func.chars().collect::>()) - .powerset() - .flatten() - .filter(|e| e.len() > 1) - .filter(|ele| { - if seen.contains(ele) { - false - } else { - seen.insert(ele.clone()); - true - } - }) - .collect::>>(); - - let mut tuple_list_1: Vec<(String, String)> = Vec::new(); - - let mut seen_2: HashSet<(String, String)> = HashSet::new(); - for ele in powerset { - for i in 1..ele.len() { - let string = ele.clone().into_iter().collect::(); - let (first, last) = string.split_at(i); - let data = (first.to_string(), last.to_string()); - if seen_2.contains(&data) { - continue; - } - seen_2.insert(data.clone()); - tuple_list_1.push(data) - } - } - - let keys: Vec<&String> = tuple_list_1.iter().map(|(a, _)| a).collect(); - let mut output = phf_codegen::Map::new(); - let mut seen_3: HashSet = HashSet::new(); - - for (key, value) in tuple_list_1.iter() { - if seen_3.contains(&*key) { - continue; - } - - seen_3.insert(key.clone()); - if keys.iter().filter(|a| a == &&key).count() == 1 { - output.entry( - key.clone(), - &format!("HintEnum::Single({}{}{})", QUOTE, value, QUOTE), - ); - } else { - let multi_data = tuple_list_1 - .iter() - .filter(|(a, _)| a == key) - .map(|(_, b)| b) - .collect::>(); - output.entry(key.clone(), &format!("HintEnum::Many(&{:?})", multi_data)); - } - } - - output -} +include!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/src/hashmap_helper.rs" +)); diff --git a/src/hashmap_helper.rs b/src/hashmap_helper.rs new file mode 100644 index 0000000..7239f8a --- /dev/null +++ b/src/hashmap_helper.rs @@ -0,0 +1,96 @@ +use std::collections::HashSet; + +#[allow(dead_code)] +pub fn compile_hashmap(data: Vec) -> Vec<(String, String)> { + let start = std::time::Instant::now(); + println!("compile_hashmap"); + + let functions_processed: Vec = data.iter().map(|e| e.to_string() + "(").collect(); + + let mut seen = HashSet::new(); + + let tuple_list_1: Vec<(String, String)> = functions_processed + .into_iter() + .map(|func| all_possible_splits(func, &mut seen)) + .flatten() + .collect(); + + let keys: Vec<&String> = tuple_list_1.iter().map(|(a, _)| a).collect(); + let mut output: Vec<(String, String)> = Vec::new(); + let mut seen_3: HashSet = HashSet::new(); + + for (key, value) in tuple_list_1.iter() { + if seen_3.contains(&*key) { + continue; + } + + seen_3.insert(key.clone()); + if keys.iter().filter(|a| a == &&key).count() == 1 { + output.push(( + key.clone(), + format!("HintEnum::Single({}{}{})", '"', value, '"'), + )); + } else { + let multi_data = tuple_list_1 + .iter() + .filter(|(a, _)| a == key) + .map(|(_, b)| b) + .collect::>(); + output.push((key.clone(), format!("HintEnum::Many(&{:?})", multi_data))); + } + } + println!("Done! {:?}", start.elapsed()); + + output +} + +#[allow(dead_code)] +fn all_possible_splits( + func: String, seen: &mut HashSet<(String, String)>, +) -> Vec<(String, String)> { + return (1..func.len()) + .map(|i| { + let (first, last) = func.split_at(i); + return (first.to_string(), last.to_string()); + }) + .map(|(first, last)| { + if seen.contains(&(first.clone(), last.clone())) { + return None; + } + seen.insert((first.to_string(), last.to_string())); + + return Some((first.to_string(), last.to_string())); + }) + .filter(|a| a.is_some()) + .map(|a| a.unwrap()) + .collect::>(); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn hashmap_gen_test() { + let data = vec!["time", "text", "test"]; + let expect = vec![ + ("t", r#"HintEnum::Many(&["ime(", "ext(", "est("])"#), + ("ti", r#"HintEnum::Single("me(")"#), + ("tim", r#"HintEnum::Single("e(")"#), + ("time", r#"HintEnum::Single("(")"#), + ("te", r#"HintEnum::Many(&["xt(", "st("])"#), + ("tex", r#"HintEnum::Single("t(")"#), + ("text", r#"HintEnum::Single("(")"#), + ("tes", r#"HintEnum::Single("t(")"#), + ("test", r#"HintEnum::Single("(")"#), + ]; + + assert_eq!( + compile_hashmap(data.iter().map(|e| e.to_string()).collect()), + expect + .iter() + .map(|(a, b)| (a.to_string(), b.to_string())) + .collect::>() + ); + } +} diff --git a/src/lib.rs b/src/lib.rs index 6c33a93..aebaa85 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,7 @@ extern crate static_assertions; mod consts; mod egui_app; mod function; +mod hashmap_helper; mod misc; mod parsing; mod suggestions; diff --git a/src/main.rs b/src/main.rs index 4ab1f5c..14bab8c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ extern crate static_assertions; mod consts; mod egui_app; mod function; +mod hashmap_helper; mod misc; mod parsing; mod suggestions; diff --git a/src/suggestions.rs b/src/suggestions.rs index 49982fe..e76e575 100644 --- a/src/suggestions.rs +++ b/src/suggestions.rs @@ -22,7 +22,7 @@ pub fn generate_hint(input: String) -> HintEnum<'static> { let len = chars.len(); - for i in (2..=5).rev().filter(|i| len >= *i) { + for i in (1..=5).rev().filter(|i| len >= *i) { if let Some(output) = get_completion(chars_take(&chars, i)) { return output; } @@ -48,11 +48,9 @@ impl ToString for HintEnum<'static> { fn to_string(&self) -> String { match self { HintEnum::Single(single_data) => single_data.to_string(), - HintEnum::Many(multi_data) => multi_data - .iter() - .map(|a| a.to_string()) - .collect::() - , + HintEnum::Many(multi_data) => { + multi_data.iter().map(|a| a.to_string()).collect::() + } HintEnum::None => String::new(), } } @@ -100,95 +98,7 @@ impl HintEnum<'static> { pub fn is_single(&self) -> bool { !self.is_multi() } } -// include!(concat!(env!("OUT_DIR"), "/codegen.rs")); - -/// Generated by build.rs -/// Manually put here so build.rs doesn't have to recalculate every time -static COMPLETION_HASHMAP: phf::Map<&'static str, HintEnum> = ::phf::Map { - key: 2980949210194914378, - disps: &[ - (0, 5), - (0, 24), - (1, 0), - (3, 14), - (51, 0), - (0, 11), - (2, 0), - (0, 29), - (3, 23), - (23, 59), - (0, 5), - (0, 7), - (39, 43), - ], - entries: &[ - ("co", HintEnum::Many(&["s(", "sh("])), - ("c", HintEnum::Many(&["os(", "osh(", "eil(", "brt("])), - ("frac", HintEnum::Single("t(")), - ("fl", HintEnum::Single("oor(")), - ("sq", HintEnum::Single("rt(")), - ("fr", HintEnum::Single("act(")), - ("sig", HintEnum::Single("num(")), - ("ac", HintEnum::Single("os(")), - ("signum", HintEnum::Single("(")), - ("ln", HintEnum::Single("(")), - ("aco", HintEnum::Single("s(")), - ("fra", HintEnum::Single("ct(")), - ("round", HintEnum::Single("(")), - ("t", HintEnum::Many(&["an(", "anh(", "runc("])), - ("s", HintEnum::Many(&["ignum(", "in(", "inh(", "qrt("])), - ("acos", HintEnum::Single("(")), - ("exp", HintEnum::Single("(")), - ("tanh", HintEnum::Single("(")), - ("lo", HintEnum::Many(&["g2(", "g10("])), - ("log10", HintEnum::Single("(")), - ("fract", HintEnum::Single("(")), - ("trun", HintEnum::Single("c(")), - ("log1", HintEnum::Single("0(")), - ("at", HintEnum::Single("an(")), - ("tr", HintEnum::Single("unc(")), - ("floor", HintEnum::Single("(")), - ("ab", HintEnum::Single("s(")), - ("si", HintEnum::Many(&["gnum(", "n(", "nh("])), - ("asi", HintEnum::Single("n(")), - ("sin", HintEnum::Many(&["(", "h("])), - ("e", HintEnum::Single("xp(")), - ("flo", HintEnum::Single("or(")), - ("ex", HintEnum::Single("p(")), - ("sqr", HintEnum::Single("t(")), - ("log2", HintEnum::Single("(")), - ("atan", HintEnum::Single("(")), - ("sinh", HintEnum::Single("(")), - ("tru", HintEnum::Single("nc(")), - ("cei", HintEnum::Single("l(")), - ("l", HintEnum::Many(&["n(", "og2(", "og10("])), - ("asin", HintEnum::Single("(")), - ("tan", HintEnum::Many(&["(", "h("])), - ("cos", HintEnum::Many(&["(", "h("])), - ("roun", HintEnum::Single("d(")), - ("as", HintEnum::Single("in(")), - ("r", HintEnum::Single("ound(")), - ("log", HintEnum::Many(&["2(", "10("])), - ("ta", HintEnum::Many(&["n(", "nh("])), - ("floo", HintEnum::Single("r(")), - ("cbrt", HintEnum::Single("(")), - ("ata", HintEnum::Single("n(")), - ("ce", HintEnum::Single("il(")), - ("abs", HintEnum::Single("(")), - ("cosh", HintEnum::Single("(")), - ("cbr", HintEnum::Single("t(")), - ("rou", HintEnum::Single("nd(")), - ("signu", HintEnum::Single("m(")), - ("a", HintEnum::Many(&["bs(", "sin(", "cos(", "tan("])), - ("sqrt", HintEnum::Single("(")), - ("ceil", HintEnum::Single("(")), - ("ro", HintEnum::Single("und(")), - ("f", HintEnum::Many(&["loor(", "ract("])), - ("sign", HintEnum::Single("um(")), - ("trunc", HintEnum::Single("(")), - ("cb", HintEnum::Single("rt(")), - ], -}; +include!(concat!(env!("OUT_DIR"), "/codegen.rs")); /// Gets completion from `COMPLETION_HASHMAP` pub fn get_completion(key: String) -> Option> {