From 8e4c7e3eb718b86892e6c102868527b74c9dedc9 Mon Sep 17 00:00:00 2001 From: Kan-Ru Chen Date: Mon, 15 Jul 2024 14:11:40 +0900 Subject: [PATCH 1/2] refactor: Merge fuzzy_search_mode and conversion_engine options --- capi/src/io.rs | 41 +++++---- capi/src/public.rs | 6 ++ include/chewing.h | 15 ++++ src/conversion/chewing.rs | 72 ++-------------- src/conversion/fuzzy.rs | 29 +++++++ src/conversion/mod.rs | 6 +- src/conversion/simple.rs | 68 +++++++++++++++ src/dictionary/layered.rs | 48 +++++++---- src/dictionary/mod.rs | 40 ++++++--- src/dictionary/sqlite.rs | 46 ++++++---- src/dictionary/trie.rs | 153 ++++++++++++++++++++------------- src/dictionary/trie_buf.rs | 31 +++++-- src/editor/mod.rs | 41 +++++---- src/editor/selection/phrase.rs | 46 ++++++++-- tests/test-bopomofo.c | 6 +- tests/test-config.c | 5 +- tests/test-error-handling.c | 1 - 17 files changed, 422 insertions(+), 232 deletions(-) create mode 100644 src/conversion/fuzzy.rs create mode 100644 src/conversion/simple.rs diff --git a/capi/src/io.rs b/capi/src/io.rs index 698915566..7c8530153 100644 --- a/capi/src/io.rs +++ b/capi/src/io.rs @@ -9,10 +9,9 @@ use std::{ }; use chewing::{ - conversion::{ChewingEngine, Interval, SimpleEngine, Symbol}, + conversion::{ChewingEngine, FuzzyChewingEngine, Interval, SimpleEngine, Symbol}, dictionary::{ - Dictionary, Layered, LoadDictionaryError, SystemDictionaryLoader, Trie, - UserDictionaryLoader, + Dictionary, Layered, LookupStrategy, SystemDictionaryLoader, Trie, UserDictionaryLoader, }, editor::{ keyboard::{AnyKeyboardLayout, KeyCode, KeyboardLayout, Modifiers, Qwerty}, @@ -28,8 +27,9 @@ use chewing::{ use log::{debug, error, info}; use crate::public::{ - ChewingConfigData, ChewingContext, IntervalType, SelKeys, CHINESE_MODE, FULLSHAPE_MODE, - HALFSHAPE_MODE, MAX_SELKEY, SYMBOL_MODE, + ChewingConfigData, ChewingContext, IntervalType, SelKeys, CHEWING_CONVERSION_ENGINE, + CHINESE_MODE, FULLSHAPE_MODE, FUZZY_CHEWING_CONVERSION_ENGINE, HALFSHAPE_MODE, MAX_SELKEY, + SIMPLE_CONVERSION_ENGINE, SYMBOL_MODE, }; use super::logger::ChewingLogger; @@ -310,7 +310,6 @@ pub unsafe extern "C" fn chewing_config_has_option( | "chewing.selection_keys" | "chewing.character_form" | "chewing.space_is_select_key" - | "chewing.fuzzy_search_mode" | "chewing.conversion_engine" | "chewing.enable_fullwidth_toggle_key" => true, _ => false, @@ -354,10 +353,10 @@ pub unsafe extern "C" fn chewing_config_get_int( CharacterForm::Fullwidth => FULLSHAPE_MODE, }, "chewing.space_is_select_key" => option.space_is_select_key as c_int, - "chewing.fuzzy_search_mode" => option.fuzzy_search as c_int, "chewing.conversion_engine" => match option.conversion_engine { - ConversionEngineKind::ChewingEngine => 0, - ConversionEngineKind::SimpleEngine => 1, + ConversionEngineKind::SimpleEngine => SIMPLE_CONVERSION_ENGINE, + ConversionEngineKind::ChewingEngine => CHEWING_CONVERSION_ENGINE, + ConversionEngineKind::FuzzyChewingEngine => FUZZY_CHEWING_CONVERSION_ENGINE, }, "chewing.enable_fullwidth_toggle_key" => option.enable_fullwidth_toggle_key as c_int, _ => ERROR, @@ -448,21 +447,25 @@ pub unsafe extern "C" fn chewing_config_set_int( ensure_bool!(value); options.space_is_select_key = value > 0; } - "chewing.fuzzy_search_mode" => { - ensure_bool!(value); - options.fuzzy_search = value > 0; - } "chewing.conversion_engine" => { options.conversion_engine = match value { - 0 => { + SIMPLE_CONVERSION_ENGINE => { + ctx.editor + .set_conversion_engine(Box::new(SimpleEngine::new())); + options.lookup_strategy = LookupStrategy::Standard; + ConversionEngineKind::SimpleEngine + } + CHEWING_CONVERSION_ENGINE => { ctx.editor .set_conversion_engine(Box::new(ChewingEngine::new())); + options.lookup_strategy = LookupStrategy::Standard; ConversionEngineKind::ChewingEngine } - 1 => { + FUZZY_CHEWING_CONVERSION_ENGINE => { ctx.editor - .set_conversion_engine(Box::new(SimpleEngine::new())); - ConversionEngineKind::SimpleEngine + .set_conversion_engine(Box::new(FuzzyChewingEngine::new())); + options.lookup_strategy = LookupStrategy::FuzzyPartialPrefix; + ConversionEngineKind::ChewingEngine } _ => return ERROR, } @@ -1138,13 +1141,13 @@ pub unsafe extern "C" fn chewing_userphrase_lookup( Some(phrase) => ctx .editor .user_dict() - .lookup_all_phrases(&syllables) + .lookup_all_phrases(&syllables, LookupStrategy::Standard) .iter() .any(|ph| ph.as_str() == phrase) as c_int, None => ctx .editor .user_dict() - .lookup_first_phrase(&syllables) + .lookup_first_phrase(&syllables, LookupStrategy::Standard) .is_some() as c_int, } } diff --git a/capi/src/public.rs b/capi/src/public.rs index ddce7afa4..e61f851e5 100644 --- a/capi/src/public.rs +++ b/capi/src/public.rs @@ -15,6 +15,12 @@ pub const SYMBOL_MODE: c_int = 0; pub const FULLSHAPE_MODE: c_int = 1; /// Indicates chewing will not translate latin and puctuation characters. pub const HALFSHAPE_MODE: c_int = 0; +/// Use conversion engine that doesn't perform intelligent phrasing. +pub const SIMPLE_CONVERSION_ENGINE: c_int = 0; +/// Use the original Chewing intelligent phrasing. +pub const CHEWING_CONVERSION_ENGINE: c_int = 1; +/// Use original Chewing intelligent phrasing with fuzzy prefix search. +pub const FUZZY_CHEWING_CONVERSION_ENGINE: c_int = 2; /// Indicates automatic user phrase learning is disabled. pub const AUTOLEARN_DISABLED: usize = 1; /// Indicates automatic user phrase learning is enabled. diff --git a/include/chewing.h b/include/chewing.h index f7534deaf..b25591fd4 100644 --- a/include/chewing.h +++ b/include/chewing.h @@ -50,6 +50,21 @@ typedef struct ChewingContext ChewingContext; */ #define HALFSHAPE_MODE 0 +/** + * Use conversion engine that doesn't perform intelligent phrasing. + */ +#define SIMPLE_CONVERSION_ENGINE 0 + +/** + * Use the original Chewing intelligent phrasing. + */ +#define CHEWING_CONVERSION_ENGINE 1 + +/** + * Use original Chewing intelligent phrasing with fuzzy prefix search. + */ +#define FUZZY_CHEWING_CONVERSION_ENGINE 2 + /** * Indicates automatic user phrase learning is disabled. */ diff --git a/src/conversion/chewing.rs b/src/conversion/chewing.rs index efb5bb687..85754fc2e 100644 --- a/src/conversion/chewing.rs +++ b/src/conversion/chewing.rs @@ -7,19 +7,23 @@ use std::{ use log::trace; -use crate::dictionary::{Dictionary, Phrase}; +use crate::dictionary::{Dictionary, LookupStrategy, Phrase}; use super::{Composition, ConversionEngine, Gap, Interval, Symbol}; /// TODO: doc #[derive(Debug, Default)] -pub struct ChewingEngine; +pub struct ChewingEngine { + pub(crate) lookup_strategy: LookupStrategy, +} impl ChewingEngine { const MAX_OUT_PATHS: usize = 100; /// TODO: doc pub fn new() -> ChewingEngine { - ChewingEngine + ChewingEngine { + lookup_strategy: LookupStrategy::Standard, + } } pub fn convert<'a>( &'a self, @@ -61,66 +65,6 @@ impl ConversionEngine for ChewingEngine { } } -/// Simple engine does not perform any intelligent conversion. -#[derive(Debug, Default)] -pub struct SimpleEngine; - -impl SimpleEngine { - pub fn new() -> SimpleEngine { - SimpleEngine - } - pub fn convert<'a>( - &'a self, - dict: &'a dyn Dictionary, - comp: &'a Composition, - ) -> impl Iterator> + Clone + 'a { - let mut intervals = vec![]; - - for (i, sym) in comp.symbols().iter().enumerate() { - if comp - .selections - .iter() - .any(|selection| selection.intersect_range(i, i + 1)) - { - continue; - } - if sym.is_char() { - intervals.push(Interval { - start: i, - end: i + 1, - is_phrase: false, - str: sym.to_char().unwrap().to_string().into_boxed_str(), - }); - } else { - let phrase = dict.lookup_first_phrase(&[sym.to_syllable().unwrap()]); - let phrase_str = phrase.map_or_else( - || sym.to_syllable().unwrap().to_string(), - |phrase| phrase.to_string(), - ); - intervals.push(Interval { - start: i, - end: i + 1, - is_phrase: true, - str: phrase_str.into_boxed_str(), - }) - } - } - intervals.extend_from_slice(comp.selections()); - intervals.sort_by_key(|int| int.start); - iter::once(intervals) - } -} - -impl ConversionEngine for SimpleEngine { - fn convert<'a>( - &'a self, - dict: &'a dyn Dictionary, - comp: &'a Composition, - ) -> Box> + 'a> { - Box::new(SimpleEngine::convert(self, dict, comp)) - } -} - fn glue_fn(com: &Composition, mut acc: Vec, interval: Interval) -> Vec { if acc.is_empty() { acc.push(interval); @@ -184,7 +128,7 @@ impl ChewingEngine { let mut max_freq = 0; let mut best_phrase = None; - 'next_phrase: for phrase in dict.lookup_all_phrases(&symbols) { + 'next_phrase: for phrase in dict.lookup_all_phrases(&symbols, self.lookup_strategy) { // If there exists a user selected interval which is a // sub-interval of this phrase but the substring is // different then we can skip this phrase. diff --git a/src/conversion/fuzzy.rs b/src/conversion/fuzzy.rs new file mode 100644 index 000000000..ef1997481 --- /dev/null +++ b/src/conversion/fuzzy.rs @@ -0,0 +1,29 @@ +use crate::dictionary::LookupStrategy; + +use super::{ChewingEngine, ConversionEngine}; + +/// TODO: doc +#[derive(Debug, Default)] +pub struct FuzzyChewingEngine { + inner: ChewingEngine, +} + +impl FuzzyChewingEngine { + pub fn new() -> FuzzyChewingEngine { + FuzzyChewingEngine { + inner: ChewingEngine { + lookup_strategy: LookupStrategy::FuzzyPartialPrefix, + }, + } + } +} + +impl ConversionEngine for FuzzyChewingEngine { + fn convert<'a>( + &'a self, + dict: &'a dyn crate::dictionary::Dictionary, + comp: &'a super::Composition, + ) -> Box> + 'a> { + Box::new(ChewingEngine::convert(&self.inner, dict, comp)) + } +} diff --git a/src/conversion/mod.rs b/src/conversion/mod.rs index 0121b6944..121f08525 100644 --- a/src/conversion/mod.rs +++ b/src/conversion/mod.rs @@ -1,6 +1,8 @@ //! Algorithms to convert syllables to Chinese characters. mod chewing; +mod fuzzy; +mod simple; mod symbol; use std::{ @@ -14,7 +16,9 @@ use crate::{ zhuyin::{Syllable, SyllableSlice}, }; -pub use self::chewing::{ChewingEngine, SimpleEngine}; +pub use self::chewing::ChewingEngine; +pub use self::fuzzy::FuzzyChewingEngine; +pub use self::simple::SimpleEngine; pub(crate) use self::symbol::{full_width_symbol_input, special_symbol_input}; pub trait ConversionEngine: Debug { diff --git a/src/conversion/simple.rs b/src/conversion/simple.rs new file mode 100644 index 000000000..375c06d10 --- /dev/null +++ b/src/conversion/simple.rs @@ -0,0 +1,68 @@ +use std::iter; + +use crate::dictionary::Dictionary; + +use super::{Composition, ConversionEngine, Interval}; + +/// Simple engine does not perform any intelligent conversion. +#[derive(Debug, Default)] +pub struct SimpleEngine; + +impl SimpleEngine { + pub fn new() -> SimpleEngine { + SimpleEngine + } + pub fn convert<'a>( + &'a self, + dict: &'a dyn Dictionary, + comp: &'a Composition, + ) -> impl Iterator> + Clone + 'a { + let mut intervals = vec![]; + + for (i, sym) in comp.symbols().iter().enumerate() { + if comp + .selections + .iter() + .any(|selection| selection.intersect_range(i, i + 1)) + { + continue; + } + if sym.is_char() { + intervals.push(Interval { + start: i, + end: i + 1, + is_phrase: false, + str: sym.to_char().unwrap().to_string().into_boxed_str(), + }); + } else { + let phrase = dict.lookup_first_phrase( + &[sym.to_syllable().unwrap()], + crate::dictionary::LookupStrategy::Standard, + ); + let phrase_str = phrase.map_or_else( + || sym.to_syllable().unwrap().to_string(), + |phrase| phrase.to_string(), + ); + intervals.push(Interval { + start: i, + end: i + 1, + is_phrase: true, + str: phrase_str.into_boxed_str(), + }) + } + } + intervals.extend_from_slice(comp.selections()); + intervals.sort_by_key(|int| int.start); + iter::once(intervals) + } +} + +impl ConversionEngine for SimpleEngine { + fn convert<'a>( + &'a self, + dict: &'a dyn Dictionary, + comp: &'a Composition, + ) -> Box> + 'a> { + Box::new(SimpleEngine::convert(self, dict, comp)) + } +} diff --git a/src/dictionary/layered.rs b/src/dictionary/layered.rs index bcf2e6e66..d3fc3dd96 100644 --- a/src/dictionary/layered.rs +++ b/src/dictionary/layered.rs @@ -8,7 +8,10 @@ use log::error; use crate::zhuyin::SyllableSlice; -use super::{Dictionary, DictionaryInfo, DictionaryMut, Entries, Phrase, UpdateDictionaryError}; +use super::{ + Dictionary, DictionaryInfo, DictionaryMut, Entries, LookupStrategy, Phrase, + UpdateDictionaryError, +}; /// A collection of dictionaries that returns the union of the lookup results. /// # Examples @@ -16,7 +19,7 @@ use super::{Dictionary, DictionaryInfo, DictionaryMut, Entries, Phrase, UpdateDi /// ``` /// # fn main() -> Result<(), Box> { /// -/// use chewing::{dictionary::{Layered, TrieBuf, Dictionary, Phrase}, syl, zhuyin::Bopomofo}; +/// use chewing::{dictionary::{Layered, TrieBuf, Dictionary, LookupStrategy, Phrase}, syl, zhuyin::Bopomofo}; /// /// let sys_dict = TrieBuf::from([( /// vec![syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4]], @@ -39,7 +42,7 @@ use super::{Dictionary, DictionaryInfo, DictionaryMut, Entries, Phrase, UpdateDi /// .collect::>(), /// dict.lookup_all_phrases(&[ /// syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4] -/// ]), +/// ], LookupStrategy::Standard), /// ); /// # Ok(()) /// # } @@ -79,7 +82,12 @@ impl Dictionary for Layered { /// Else /// Add phrases <- (phrase, freq) /// ``` - fn lookup_first_n_phrases(&self, syllables: &dyn SyllableSlice, first: usize) -> Vec { + fn lookup_first_n_phrases( + &self, + syllables: &dyn SyllableSlice, + first: usize, + strategy: LookupStrategy, + ) -> Vec { let mut sort_map: BTreeMap = BTreeMap::new(); let mut phrases: Vec = Vec::new(); @@ -87,7 +95,7 @@ impl Dictionary for Layered { .iter() .chain(iter::once(&self.user_dict)) .for_each(|d| { - for phrase in d.lookup_all_phrases(syllables) { + for phrase in d.lookup_all_phrases(syllables, strategy) { debug_assert!(!phrase.as_str().is_empty()); match sort_map.entry(phrase.to_string()) { Entry::Occupied(entry) => { @@ -128,13 +136,6 @@ impl Dictionary for Layered { None } - fn set_lookup_strategy(&mut self, strategy: super::LookupStrategy) { - self.sys_dict - .iter_mut() - .for_each(|sys_dict| sys_dict.set_lookup_strategy(strategy)); - self.user_dict.set_lookup_strategy(strategy); - } - fn as_dict_mut(&mut self) -> Option<&mut dyn DictionaryMut> { self.user_dict.as_dict_mut() } @@ -213,7 +214,8 @@ mod tests { use crate::{ dictionary::{ - Dictionary, DictionaryBuilder, DictionaryMut, Phrase, Trie, TrieBuf, TrieBuilder, + Dictionary, DictionaryBuilder, DictionaryMut, LookupStrategy, Phrase, Trie, TrieBuf, + TrieBuilder, }, syl, zhuyin::Bopomofo, @@ -277,7 +279,10 @@ mod tests { let dict = Layered::new(vec![Box::new(sys_dict)], Box::new(user_dict)); assert_eq!( Some(("側", 1, 0).into()), - dict.lookup_first_phrase(&vec![syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4]]), + dict.lookup_first_phrase( + &vec![syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4]], + LookupStrategy::Standard + ), ); assert_eq!( [ @@ -288,7 +293,10 @@ mod tests { ] .into_iter() .collect::>(), - dict.lookup_all_phrases(&vec![syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4]]), + dict.lookup_all_phrases( + &vec![syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4]], + LookupStrategy::Standard + ), ); Ok(()) } @@ -316,7 +324,10 @@ mod tests { let mut dict = Layered::new(vec![Box::new(sys_dict)], Box::new(user_dict)); assert_eq!( Some(("側", 1, 0).into()), - dict.lookup_first_phrase(&vec![syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4]]), + dict.lookup_first_phrase( + &vec![syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4]], + LookupStrategy::Standard + ), ); assert_eq!( [ @@ -327,7 +338,10 @@ mod tests { ] .into_iter() .collect::>(), - dict.lookup_all_phrases(&vec![syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4]]), + dict.lookup_all_phrases( + &vec![syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4]], + LookupStrategy::Standard + ), ); let _ = dict.about(); assert!(dict.as_dict_mut().is_none()); diff --git a/src/dictionary/mod.rs b/src/dictionary/mod.rs index fa308b2da..8b0ba77a5 100644 --- a/src/dictionary/mod.rs +++ b/src/dictionary/mod.rs @@ -262,14 +262,14 @@ impl Display for Phrase { /// # Examples /// /// ``` -/// use chewing::{dictionary::{Dictionary, TrieBuf}, syl, zhuyin::Bopomofo}; +/// use chewing::{dictionary::{Dictionary, LookupStrategy, TrieBuf}, syl, zhuyin::Bopomofo}; /// /// let dict = TrieBuf::from([ /// (vec![syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4]], vec![("測", 100)]), /// ]); /// /// for phrase in dict.lookup_all_phrases( -/// &[syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4]] +/// &[syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4]], LookupStrategy::Standard /// ) { /// assert_eq!("測", phrase.as_str()); /// assert_eq!(100, phrase.freq()); @@ -300,9 +300,10 @@ pub type Entries<'a> = Box, Phrase)> + 'a>; /// /// If the dictionary supports the lookup strategy it should try to use. /// Otherwise fallback to standard. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] pub enum LookupStrategy { /// The native lookup strategy supported by the dictionary. + #[default] Standard, /// Try to fuzzy match partial syllables using only preffix. FuzzyPartialPrefix, @@ -319,13 +320,13 @@ pub enum LookupStrategy { /// ``` /// # fn main() -> Result<(), Box> { /// -/// use chewing::{dictionary::{Dictionary, DictionaryMut, TrieBuf}, syl, zhuyin::Bopomofo}; +/// use chewing::{dictionary::{Dictionary, DictionaryMut, LookupStrategy, TrieBuf}, syl, zhuyin::Bopomofo}; /// /// let mut dict = TrieBuf::new_in_memory(); /// dict.add_phrase(&[syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4]], ("測", 100).into())?; /// /// for phrase in dict.lookup_all_phrases( -/// &[syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4]] +/// &[syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4]], LookupStrategy::Standard /// ) { /// assert_eq!("測", phrase.as_str()); /// assert_eq!(100, phrase.freq()); @@ -337,18 +338,33 @@ pub trait Dictionary: Debug { /// Returns first N phrases matched by the syllables. /// /// The result should use a stable order each time for the same input. - fn lookup_first_n_phrases(&self, syllables: &dyn SyllableSlice, first: usize) -> Vec; + fn lookup_first_n_phrases( + &self, + syllables: &dyn SyllableSlice, + first: usize, + strategy: LookupStrategy, + ) -> Vec; /// Returns the first phrase matched by the syllables. /// /// The result should use a stable order each time for the same input. - fn lookup_first_phrase(&self, syllables: &dyn SyllableSlice) -> Option { - self.lookup_first_n_phrases(syllables, 1).into_iter().next() + fn lookup_first_phrase( + &self, + syllables: &dyn SyllableSlice, + strategy: LookupStrategy, + ) -> Option { + self.lookup_first_n_phrases(syllables, 1, strategy) + .into_iter() + .next() } /// Returns all phrases matched by the syllables. /// /// The result should use a stable order each time for the same input. - fn lookup_all_phrases(&self, syllables: &dyn SyllableSlice) -> Vec { - self.lookup_first_n_phrases(syllables, usize::MAX) + fn lookup_all_phrases( + &self, + syllables: &dyn SyllableSlice, + strategy: LookupStrategy, + ) -> Vec { + self.lookup_first_n_phrases(syllables, usize::MAX, strategy) } /// Returns an iterator to all phrases in the dictionary. fn entries(&self) -> Entries<'_>; @@ -356,10 +372,6 @@ pub trait Dictionary: Debug { fn about(&self) -> DictionaryInfo; /// Returns the dictionary file path if it's backed by a file. fn path(&self) -> Option<&Path>; - /// Set the lookup strategy hint. - fn set_lookup_strategy(&mut self, strategy: LookupStrategy) { - _ = strategy - } fn as_dict_mut(&mut self) -> Option<&mut dyn DictionaryMut>; } diff --git a/src/dictionary/sqlite.rs b/src/dictionary/sqlite.rs index 9b3a6350c..b5f547cf5 100644 --- a/src/dictionary/sqlite.rs +++ b/src/dictionary/sqlite.rs @@ -12,7 +12,7 @@ use crate::zhuyin::{Syllable, SyllableSlice}; use super::{ BuildDictionaryError, Dictionary, DictionaryBuilder, DictionaryInfo, DictionaryMut, Entries, - Phrase, UpdateDictionaryError, + LookupStrategy, Phrase, UpdateDictionaryError, }; const APPLICATION_ID: u32 = 0x43484557; // 'CHEW' in big-endian @@ -298,7 +298,13 @@ impl From for UpdateDictionaryError { } impl Dictionary for SqliteDictionary { - fn lookup_first_n_phrases(&self, syllables: &dyn SyllableSlice, first: usize) -> Vec { + fn lookup_first_n_phrases( + &self, + syllables: &dyn SyllableSlice, + first: usize, + strategy: LookupStrategy, + ) -> Vec { + let _ = strategy; let syllables_bytes = syllables.to_bytes(); let mut stmt = self .conn @@ -583,7 +589,8 @@ mod tests { use crate::{ dictionary::{ - Dictionary, DictionaryBuilder, DictionaryMut, Phrase, SqliteDictionaryBuilder, + Dictionary, DictionaryBuilder, DictionaryMut, LookupStrategy, Phrase, + SqliteDictionaryBuilder, }, syl, zhuyin::Bopomofo, @@ -636,10 +643,13 @@ mod tests { Phrase::new("策士", 9318).with_time(186613), Phrase::new("測試", 9318).with_time(186613) ], - dict.lookup_all_phrases(&[ - syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4], - syl![Bopomofo::SH, Bopomofo::TONE4], - ]) + dict.lookup_all_phrases( + &[ + syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4], + syl![Bopomofo::SH, Bopomofo::TONE4], + ], + LookupStrategy::Standard + ) ); } @@ -670,10 +680,13 @@ mod tests { )?; assert_eq!( vec![Phrase::new("測試", 9900).with_time(0)], - dict.lookup_all_phrases(&[ - syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4], - syl![Bopomofo::SH, Bopomofo::TONE4], - ]) + dict.lookup_all_phrases( + &[ + syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4], + syl![Bopomofo::SH, Bopomofo::TONE4], + ], + LookupStrategy::Standard + ) ); Ok(()) } @@ -689,10 +702,13 @@ mod tests { dict.update_phrase(&syllables, ("測試", 9318).into(), 9900, 0)?; assert_eq!( vec![Phrase::new("測試", 9900).with_time(0)], - dict.lookup_all_phrases(&[ - syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4], - syl![Bopomofo::SH, Bopomofo::TONE4], - ]) + dict.lookup_all_phrases( + &[ + syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4], + syl![Bopomofo::SH, Bopomofo::TONE4], + ], + LookupStrategy::Standard + ) ); Ok(()) } diff --git a/src/dictionary/trie.rs b/src/dictionary/trie.rs index 9c9a792c5..cb3bc570f 100644 --- a/src/dictionary/trie.rs +++ b/src/dictionary/trie.rs @@ -21,7 +21,10 @@ use log::{error, warn}; use crate::zhuyin::{Syllable, SyllableSlice}; -use super::{BuildDictionaryError, Dictionary, DictionaryBuilder, DictionaryInfo, Entries, Phrase}; +use super::{ + BuildDictionaryError, Dictionary, DictionaryBuilder, DictionaryInfo, Entries, LookupStrategy, + Phrase, +}; const DICT_FORMAT_VERSION: u8 = 0; @@ -79,7 +82,7 @@ impl TrieLeafView<'_> { /// /// use chewing::{syl, zhuyin::{Bopomofo, Syllable}}; /// # use chewing::dictionary::{DictionaryBuilder, TrieBuilder}; -/// use chewing::dictionary::{Dictionary, Trie}; +/// use chewing::dictionary::{Dictionary, LookupStrategy, Trie}; /// # let mut tempfile = File::create("dict.dat")?; /// # let mut builder = TrieBuilder::new(); /// # builder.insert(&[ @@ -95,7 +98,7 @@ impl TrieLeafView<'_> { /// let mut phrase = dict.lookup_first_phrase(&[ /// syl![Bopomofo::Z, Bopomofo::TONE4], /// syl![Bopomofo::D, Bopomofo::I, Bopomofo::AN, Bopomofo::TONE3] -/// ]); +/// ], LookupStrategy::Standard); /// assert_eq!("字典", phrase.unwrap().as_str()); /// # Ok(()) /// # } @@ -263,7 +266,12 @@ macro_rules! iter_bail_if_oob { } impl Dictionary for Trie { - fn lookup_first_n_phrases(&self, syllables: &dyn SyllableSlice, first: usize) -> Vec { + fn lookup_first_n_phrases( + &self, + syllables: &dyn SyllableSlice, + first: usize, + strategy: LookupStrategy, + ) -> Vec { let dict = self.index.as_ref(); let data = self.phrase_seq.as_ref(); @@ -276,9 +284,9 @@ impl Dictionary for Trie { return vec![]; } - let search_predicate = match self.fuzzy_search { - false => |n: u16, syl: &Syllable| n == syl.to_u16(), - true => |n: u16, syl: &Syllable| { + let search_predicate = match strategy { + LookupStrategy::Standard => |n: u16, syl: &Syllable| n == syl.to_u16(), + LookupStrategy::FuzzyPartialPrefix => |n: u16, syl: &Syllable| { if n == 0 { return false; } @@ -429,13 +437,6 @@ impl Dictionary for Trie { self.path.as_ref().map(|p| p as &Path) } - fn set_lookup_strategy(&mut self, strategy: super::LookupStrategy) { - match strategy { - super::LookupStrategy::Standard => self.enable_fuzzy_search(false), - super::LookupStrategy::FuzzyPartialPrefix => self.enable_fuzzy_search(true), - } - } - fn as_dict_mut(&mut self) -> Option<&mut dyn super::DictionaryMut> { None } @@ -1205,8 +1206,8 @@ mod tests { use crate::{ dictionary::{ - trie::TrieBuilderNode, Dictionary, DictionaryBuilder, DictionaryInfo, Phrase, - TrieOpenOptions, + trie::TrieBuilderNode, Dictionary, DictionaryBuilder, DictionaryInfo, LookupStrategy, + Phrase, TrieOpenOptions, }, syl, zhuyin::Bopomofo, @@ -1298,7 +1299,10 @@ mod tests { let dict = Trie::new(&mut cursor)?; assert_eq!( vec![Phrase::new("測", 1), Phrase::new("冊", 1)], - dict.lookup_all_phrases(&[syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4]]) + dict.lookup_all_phrases( + &[syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4]], + LookupStrategy::Standard + ) ); Ok(()) @@ -1318,16 +1322,17 @@ mod tests { let mut cursor = Cursor::new(vec![]); builder.write(&mut cursor)?; cursor.rewind()?; - let dict = TrieOpenOptions::new() - .fuzzy_search(true) - .read_from(&mut cursor)?; + let dict = TrieOpenOptions::new().read_from(&mut cursor)?; assert_eq!( vec![Phrase::new("測", 1), Phrase::new("冊", 1)], - dict.lookup_all_phrases(&[syl![Bopomofo::C, Bopomofo::E]]) + dict.lookup_all_phrases( + &[syl![Bopomofo::C, Bopomofo::E]], + LookupStrategy::FuzzyPartialPrefix + ) ); assert_eq!( vec![Phrase::new("測", 1), Phrase::new("冊", 1)], - dict.lookup_all_phrases(&[syl![Bopomofo::C]]) + dict.lookup_all_phrases(&[syl![Bopomofo::C]], LookupStrategy::FuzzyPartialPrefix) ); Ok(()) @@ -1365,26 +1370,35 @@ mod tests { let dict = Trie::new(&mut cursor)?; assert_eq!( vec![Phrase::new("策試", 2), Phrase::new("測試", 1)], - dict.lookup_all_phrases(&[ - syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4], - syl![Bopomofo::SH, Bopomofo::TONE4] - ]) + dict.lookup_all_phrases( + &[ + syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4], + syl![Bopomofo::SH, Bopomofo::TONE4] + ], + LookupStrategy::Standard + ) ); assert_eq!( vec![Phrase::new("測試成功", 3)], - dict.lookup_all_phrases(&[ - syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4], - syl![Bopomofo::SH, Bopomofo::TONE4], - syl![Bopomofo::CH, Bopomofo::ENG, Bopomofo::TONE2], - syl![Bopomofo::G, Bopomofo::U, Bopomofo::ENG], - ]) + dict.lookup_all_phrases( + &[ + syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4], + syl![Bopomofo::SH, Bopomofo::TONE4], + syl![Bopomofo::CH, Bopomofo::ENG, Bopomofo::TONE2], + syl![Bopomofo::G, Bopomofo::U, Bopomofo::ENG], + ], + LookupStrategy::Standard + ) ); assert_eq!( Vec::::new(), - dict.lookup_all_phrases(&[ - syl![Bopomofo::C, Bopomofo::U, Bopomofo::O, Bopomofo::TONE4], - syl![Bopomofo::U, Bopomofo::TONE4] - ]) + dict.lookup_all_phrases( + &[ + syl![Bopomofo::C, Bopomofo::U, Bopomofo::O, Bopomofo::TONE4], + syl![Bopomofo::U, Bopomofo::TONE4] + ], + LookupStrategy::Standard + ) ); Ok(()) @@ -1424,36 +1438,51 @@ mod tests { .read_from(&mut cursor)?; assert_eq!( vec![Phrase::new("策試", 2), Phrase::new("測試", 1)], - dict.lookup_all_phrases(&[syl![Bopomofo::C, Bopomofo::E], syl![Bopomofo::SH]]) + dict.lookup_all_phrases( + &[syl![Bopomofo::C, Bopomofo::E], syl![Bopomofo::SH]], + LookupStrategy::FuzzyPartialPrefix + ) ); assert_eq!( vec![Phrase::new("策試", 2), Phrase::new("測試", 1)], - dict.lookup_all_phrases(&[syl![Bopomofo::C], syl![Bopomofo::SH]]) + dict.lookup_all_phrases( + &[syl![Bopomofo::C], syl![Bopomofo::SH]], + LookupStrategy::FuzzyPartialPrefix + ) ); assert_eq!( vec![Phrase::new("測試成功", 3)], - dict.lookup_all_phrases(&[ - syl![Bopomofo::C, Bopomofo::E], - syl![Bopomofo::SH], - syl![Bopomofo::CH, Bopomofo::ENG], - syl![Bopomofo::G, Bopomofo::U, Bopomofo::ENG], - ]) + dict.lookup_all_phrases( + &[ + syl![Bopomofo::C, Bopomofo::E], + syl![Bopomofo::SH], + syl![Bopomofo::CH, Bopomofo::ENG], + syl![Bopomofo::G, Bopomofo::U, Bopomofo::ENG], + ], + LookupStrategy::FuzzyPartialPrefix + ) ); assert_eq!( vec![Phrase::new("測試成功", 3)], - dict.lookup_all_phrases(&[ - syl![Bopomofo::C], - syl![Bopomofo::SH], - syl![Bopomofo::CH], - syl![Bopomofo::G], - ]) + dict.lookup_all_phrases( + &[ + syl![Bopomofo::C], + syl![Bopomofo::SH], + syl![Bopomofo::CH], + syl![Bopomofo::G], + ], + LookupStrategy::FuzzyPartialPrefix + ) ); assert_eq!( Vec::::new(), - dict.lookup_all_phrases(&[ - syl![Bopomofo::C, Bopomofo::U, Bopomofo::O, Bopomofo::TONE4], - syl![Bopomofo::U, Bopomofo::TONE4] - ]) + dict.lookup_all_phrases( + &[ + syl![Bopomofo::C, Bopomofo::U, Bopomofo::O, Bopomofo::TONE4], + syl![Bopomofo::U, Bopomofo::TONE4] + ], + LookupStrategy::FuzzyPartialPrefix + ) ); Ok(()) @@ -1502,7 +1531,10 @@ mod tests { Phrase::new("測", 0), Phrase::new("側", 0), ], - dict.lookup_all_phrases(&[syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4],]) + dict.lookup_all_phrases( + &[syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4],], + LookupStrategy::Standard + ) ); Ok(()) } @@ -1557,10 +1589,13 @@ mod tests { Phrase::new("側視", 318), Phrase::new("側室", 318), ], - dict.lookup_all_phrases(&[ - syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4], - syl![Bopomofo::SH, Bopomofo::TONE4], - ]) + dict.lookup_all_phrases( + &[ + syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4], + syl![Bopomofo::SH, Bopomofo::TONE4], + ], + LookupStrategy::Standard + ) ); Ok(()) } diff --git a/src/dictionary/trie_buf.rs b/src/dictionary/trie_buf.rs index 8d4f59a42..b043c4ff2 100644 --- a/src/dictionary/trie_buf.rs +++ b/src/dictionary/trie_buf.rs @@ -13,7 +13,7 @@ use crate::zhuyin::{Syllable, SyllableSlice}; use super::{ BuildDictionaryError, Dictionary, DictionaryBuilder, DictionaryInfo, DictionaryMut, Entries, - Phrase, Trie, TrieBuilder, UpdateDictionaryError, + LookupStrategy, Phrase, Trie, TrieBuilder, UpdateDictionaryError, }; #[derive(Debug)] @@ -76,6 +76,7 @@ impl TrieBuf { pub(crate) fn entries_iter_for<'a>( &'a self, syllables: &'a dyn SyllableSlice, + strategy: LookupStrategy, ) -> impl Iterator + 'a { let syllable_key = Cow::from(syllables.to_slice().into_owned()); let min_key = (syllable_key.clone(), Cow::from(MIN_PHRASE)); @@ -83,7 +84,7 @@ impl TrieBuf { let store_iter = self .trie .iter() - .flat_map(move |trie| trie.lookup_all_phrases(syllables)); + .flat_map(move |trie| trie.lookup_all_phrases(syllables, strategy)); let btree_iter = self .btree .range(min_key..max_key) @@ -127,11 +128,12 @@ impl TrieBuf { &self, syllables: &dyn SyllableSlice, first: usize, + strategy: LookupStrategy, ) -> Vec { let mut sort_map = BTreeMap::new(); let mut phrases: Vec = Vec::new(); - for phrase in self.entries_iter_for(syllables) { + for phrase in self.entries_iter_for(syllables, strategy) { match sort_map.entry(phrase.to_string()) { Entry::Occupied(entry) => { let index = *entry.get(); @@ -158,7 +160,7 @@ impl TrieBuf { ) -> Result<(), UpdateDictionaryError> { let syllable_slice = syllables.to_slice(); if self - .entries_iter_for(&syllable_slice.as_ref()) + .entries_iter_for(&syllable_slice.as_ref(), LookupStrategy::Standard) .any(|ph| ph.as_str() == phrase.as_str()) { return Err(UpdateDictionaryError { source: None }); @@ -293,8 +295,13 @@ impl From for UpdateDictionaryError { } impl Dictionary for TrieBuf { - fn lookup_first_n_phrases(&self, syllables: &dyn SyllableSlice, first: usize) -> Vec { - TrieBuf::lookup_first_n_phrases(self, syllables, first) + fn lookup_first_n_phrases( + &self, + syllables: &dyn SyllableSlice, + first: usize, + strategy: LookupStrategy, + ) -> Vec { + TrieBuf::lookup_first_n_phrases(self, syllables, first, strategy) } fn entries(&self) -> Entries<'_> { @@ -381,7 +388,7 @@ mod tests { use std::error::Error; use crate::{ - dictionary::{DictionaryMut, Phrase}, + dictionary::{DictionaryMut, LookupStrategy, Phrase}, syl, zhuyin::Bopomofo::*, }; @@ -401,7 +408,10 @@ mod tests { assert_eq!("Unknown", info.copyright); assert_eq!( Some(("dict", 1, 2).into()), - dict.lookup_first_phrase(&[syl![Z, TONE4], syl![D, I, AN, TONE3]]) + dict.lookup_first_phrase( + &[syl![Z, TONE4], syl![D, I, AN, TONE3]], + LookupStrategy::Standard + ) ); Ok(()) } @@ -424,7 +434,10 @@ mod tests { assert_eq!("Unknown", info.copyright); assert_eq!( Some(("dict", 1, 2).into()), - dict.lookup_first_phrase(&[syl![Z, TONE4], syl![D, I, AN, TONE3]]) + dict.lookup_first_phrase( + &[syl![Z, TONE4], syl![D, I, AN, TONE3]], + LookupStrategy::Standard + ) ); Ok(()) } diff --git a/src/editor/mod.rs b/src/editor/mod.rs index b252ae01c..8d63863dc 100644 --- a/src/editor/mod.rs +++ b/src/editor/mod.rs @@ -58,8 +58,9 @@ pub enum UserPhraseAddDirection { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ConversionEngineKind { - ChewingEngine, SimpleEngine, + ChewingEngine, + FuzzyChewingEngine, } #[derive(Debug, Clone, Copy)] @@ -75,7 +76,7 @@ pub struct EditorOptions { pub language_mode: LanguageMode, pub character_form: CharacterForm, pub user_phrase_add_dir: UserPhraseAddDirection, - pub fuzzy_search: bool, + pub lookup_strategy: LookupStrategy, pub conversion_engine: ConversionEngineKind, pub enable_fullwidth_toggle_key: bool, } @@ -94,7 +95,7 @@ impl Default for EditorOptions { language_mode: LanguageMode::Chinese, character_form: CharacterForm::Halfwidth, user_phrase_add_dir: UserPhraseAddDirection::Forward, - fuzzy_search: false, + lookup_strategy: LookupStrategy::Standard, // FIXME may be out of sync with the engine used conversion_engine: ConversionEngineKind::ChewingEngine, enable_fullwidth_toggle_key: true, @@ -255,14 +256,6 @@ impl Editor { if self.shared.options.language_mode != options.language_mode { self.shared.syl.clear(); } - if self.shared.options.fuzzy_search != options.fuzzy_search { - self.shared - .dict - .set_lookup_strategy(match options.fuzzy_search { - true => LookupStrategy::FuzzyPartialPrefix, - false => LookupStrategy::Standard, - }) - } self.shared.options = options; } pub fn switch_character_form(&mut self) { @@ -579,7 +572,7 @@ impl SharedState { if self .dict .user_dict() - .lookup_all_phrases(&syllables) + .lookup_all_phrases(&syllables, LookupStrategy::Standard) .into_iter() .any(|it| it.as_str() == phrase) { @@ -610,7 +603,9 @@ impl SharedState { ); return Err(UpdateDictionaryError::new()); } - let phrases = self.dict.lookup_all_phrases(syllables); + let phrases = self + .dict + .lookup_all_phrases(syllables, LookupStrategy::Standard); if phrases.is_empty() { self.dict.add_phrase(syllables, (phrase, 1).into())?; return Ok(()); @@ -1155,14 +1150,18 @@ impl State for EnteringSyllable { self.start_entering() } _ => { - let key_behavior = match shared.options.fuzzy_search { - true => shared.syl.fuzzy_key_press(ev), - false => shared.syl.key_press(ev), + let key_behavior = match shared.options.lookup_strategy { + LookupStrategy::FuzzyPartialPrefix => shared.syl.fuzzy_key_press(ev), + LookupStrategy::Standard => shared.syl.key_press(ev), }; match key_behavior { KeyBehavior::Absorb => self.spin_absorb(), KeyBehavior::Fuzzy(syl) => { - if shared.dict.lookup_first_phrase(&[syl]).is_some() { + if shared + .dict + .lookup_first_phrase(&[syl], shared.options.lookup_strategy) + .is_some() + { shared.com.insert(Symbol::from(syl)); } self.spin_absorb() @@ -1170,7 +1169,10 @@ impl State for EnteringSyllable { KeyBehavior::Commit => { if shared .dict - .lookup_first_phrase(&[shared.syl.read()]) + .lookup_first_phrase( + &[shared.syl.read()], + shared.options.lookup_strategy, + ) .is_some() { shared.com.insert(Symbol::from(shared.syl.read())); @@ -1199,6 +1201,7 @@ impl Selecting { let mut sel = PhraseSelector::new( !editor.options.phrase_choice_rearward, + editor.options.lookup_strategy, editor.com.to_composition(), ); sel.init(editor.cursor(), &editor.dict); @@ -1351,6 +1354,7 @@ impl State for Selecting { if sym.is_syllable() { let mut sel = PhraseSelector::new( !shared.options.phrase_choice_rearward, + shared.options.lookup_strategy, shared.com.to_composition(), ); sel.init(shared.cursor(), &shared.dict); @@ -1376,6 +1380,7 @@ impl State for Selecting { if sym.is_syllable() { let mut sel = PhraseSelector::new( !shared.options.phrase_choice_rearward, + shared.options.lookup_strategy, shared.com.to_composition(), ); sel.init(shared.cursor(), &shared.dict); diff --git a/src/editor/selection/phrase.rs b/src/editor/selection/phrase.rs index 1615815f4..6242e27ed 100644 --- a/src/editor/selection/phrase.rs +++ b/src/editor/selection/phrase.rs @@ -2,7 +2,7 @@ use std::cmp::min; use crate::{ conversion::{Composition, Gap, Interval}, - dictionary::{Dictionary, Layered}, + dictionary::{Dictionary, Layered, LookupStrategy}, editor::{EditorError, SharedState}, }; @@ -12,16 +12,22 @@ pub(crate) struct PhraseSelector { end: usize, forward_select: bool, orig: usize, + lookup_strategy: LookupStrategy, com: Composition, } impl PhraseSelector { - pub(crate) fn new(forward_select: bool, com: Composition) -> PhraseSelector { + pub(crate) fn new( + forward_select: bool, + lookup_strategy: LookupStrategy, + com: Composition, + ) -> PhraseSelector { PhraseSelector { begin: 0, end: com.len(), forward_select, orig: 0, + lookup_strategy, com, } } @@ -45,7 +51,10 @@ impl PhraseSelector { !syllables.is_empty(), "should not enter here if there's no syllable in range" ); - if dict.lookup_first_phrase(&syllables).is_some() { + if dict + .lookup_first_phrase(&syllables, self.lookup_strategy) + .is_some() + { break; } if self.forward_select { @@ -75,7 +84,10 @@ impl PhraseSelector { } } let syllables = &self.com.symbols()[begin..end]; - if dict.lookup_first_phrase(&syllables).is_some() { + if dict + .lookup_first_phrase(&syllables, self.lookup_strategy) + .is_some() + { return Some((begin, end)); } } @@ -101,7 +113,10 @@ impl PhraseSelector { } } let syllables = &self.com.symbols()[begin..end]; - if dict.lookup_first_phrase(&syllables).is_some() { + if dict + .lookup_first_phrase(&syllables, self.lookup_strategy) + .is_some() + { return Some((begin, end)); } } @@ -153,7 +168,10 @@ impl PhraseSelector { } } let syllables = &self.com.symbols()[self.begin..self.end]; - if dict.lookup_first_phrase(&syllables).is_some() { + if dict + .lookup_first_phrase(&syllables, self.lookup_strategy) + .is_some() + { break; } } @@ -198,7 +216,10 @@ impl PhraseSelector { pub(crate) fn candidates(&self, editor: &SharedState, dict: &Layered) -> Vec { let mut candidates = dict - .lookup_all_phrases(&&self.com.symbols()[self.begin..self.end]) + .lookup_all_phrases( + &&self.com.symbols()[self.begin..self.end], + self.lookup_strategy, + ) .into_iter() .map(|phrase| phrase.into()) .collect::>(); @@ -208,7 +229,7 @@ impl PhraseSelector { .alt_syllables(self.com.symbol(self.begin).unwrap().to_syllable().unwrap()); for &syl in alt { candidates.extend( - dict.lookup_all_phrases(&[syl]) + dict.lookup_all_phrases(&[syl], self.lookup_strategy) .into_iter() .map(|ph| ph.into()), ) @@ -231,7 +252,7 @@ impl PhraseSelector { mod tests { use crate::{ conversion::{Composition, Interval, Symbol}, - dictionary::TrieBuf, + dictionary::{LookupStrategy, TrieBuf}, syl, zhuyin::Bopomofo::*, }; @@ -247,6 +268,7 @@ mod tests { end: 1, forward_select: false, orig: 0, + lookup_strategy: LookupStrategy::Standard, com, }; let dict = TrieBuf::from([(vec![syl![C, E, TONE4]], vec![("測", 100)])]); @@ -266,6 +288,7 @@ mod tests { end: 1, forward_select: false, orig: 0, + lookup_strategy: LookupStrategy::Standard, com, }; let dict = TrieBuf::from([(vec![syl![C, E, TONE4]], vec![("測", 100)])]); @@ -281,6 +304,7 @@ mod tests { end: 1, forward_select: true, orig: 0, + lookup_strategy: LookupStrategy::Standard, com, }; let dict = TrieBuf::from([(vec![syl![C, E, TONE4]], vec![("測", 100)])]); @@ -300,6 +324,7 @@ mod tests { end: 1, forward_select: true, orig: 0, + lookup_strategy: LookupStrategy::Standard, com, }; let dict = TrieBuf::from([(vec![syl![C, E, TONE4]], vec![("測", 100)])]); @@ -320,6 +345,7 @@ mod tests { end: 2, forward_select: false, orig: 0, + lookup_strategy: LookupStrategy::Standard, com, }; @@ -339,6 +365,7 @@ mod tests { end: 2, forward_select: false, orig: 0, + lookup_strategy: LookupStrategy::Standard, com, }; @@ -367,6 +394,7 @@ mod tests { end: 2, forward_select: false, orig: 0, + lookup_strategy: LookupStrategy::Standard, com, }; diff --git a/tests/test-bopomofo.c b/tests/test-bopomofo.c index cd4ed216e..f708f44e3 100644 --- a/tests/test-bopomofo.c +++ b/tests/test-bopomofo.c @@ -1358,7 +1358,7 @@ void test_FuzzySearchMode() ctx = chewing_new(); start_testcase(ctx, fd); chewing_set_maxChiSymbolLen(ctx, 16); - chewing_config_set_int(ctx, "chewing.fuzzy_search_mode", 1); + chewing_config_set_int(ctx, "chewing.conversion_engine", FUZZY_CHEWING_CONVERSION_ENGINE); for (i = 0; i < ARRAY_SIZE(FUZZY_INPUT); ++i) { type_keystroke_by_string(ctx, FUZZY_INPUT[i].token); @@ -1382,7 +1382,7 @@ void test_FuzzySearchMode_Hanyu() start_testcase(ctx, fd); chewing_set_maxChiSymbolLen(ctx, 16); chewing_set_KBType(ctx, KB_HANYU_PINYIN); - chewing_config_set_int(ctx, "chewing.fuzzy_search_mode", 1); + chewing_config_set_int(ctx, "chewing.conversion_engine", FUZZY_CHEWING_CONVERSION_ENGINE); for (i = 0; i < ARRAY_SIZE(FUZZY_INPUT); ++i) { type_keystroke_by_string(ctx, FUZZY_INPUT[i].token); @@ -1404,7 +1404,7 @@ void test_SimpleEngine() ctx = chewing_new(); start_testcase(ctx, fd); chewing_set_maxChiSymbolLen(ctx, 16); - chewing_config_set_int(ctx, "chewing.conversion_engine", 1); + chewing_config_set_int(ctx, "chewing.conversion_engine", SIMPLE_CONVERSION_ENGINE); for (i = 0; i < ARRAY_SIZE(SIMPLE_INPUT); ++i) { type_keystroke_by_string(ctx, SIMPLE_INPUT[i].token); diff --git a/tests/test-config.c b/tests/test-config.c index e166d8a6d..c3263377f 100644 --- a/tests/test-config.c +++ b/tests/test-config.c @@ -54,7 +54,6 @@ void test_has_option() ,"chewing.selection_keys" ,"chewing.character_form" ,"chewing.space_is_select_key" - ,"chewing.fuzzy_search_mode" ,"chewing.conversion_engine" ,"chewing.enable_fullwidth_toggle_key" }; @@ -165,8 +164,8 @@ void test_default_value_options() ok(chewing_config_get_int(ctx, "chewing.character_form") == HALFSHAPE_MODE, "default chewing.character_form shall be %d", HALFSHAPE_MODE); - ok(chewing_config_get_int(ctx, "chewing.fuzzy_search_mode") == 0, - "default chewing.fuzzy_search_mode shall be 0"); + ok(chewing_config_get_int(ctx, "chewing.conversion_engine") == 1, + "default chewing.fuzzy_search_mode shall be 1"); chewing_delete(ctx); } diff --git a/tests/test-error-handling.c b/tests/test-error-handling.c index 72c1986aa..e9d5a48ec 100644 --- a/tests/test-error-handling.c +++ b/tests/test-error-handling.c @@ -343,7 +343,6 @@ void test_FallbackDictionary() ctx = chewing_new(); start_testcase(ctx, fd); chewing_set_maxChiSymbolLen(ctx, 16); - chewing_config_set_int(ctx, "chewing.conversion_engine", 1); for (i = 0; i < ARRAY_SIZE(SIMPLE_INPUT); ++i) { type_keystroke_by_string(ctx, SIMPLE_INPUT[i].token); From 8c59342652621ac4d61d5dd95fa4757e4e5d4b8b Mon Sep 17 00:00:00 2001 From: Kan-Ru Chen Date: Mon, 15 Jul 2024 14:20:45 +0900 Subject: [PATCH 2/2] test: add test for simple engine symbol input --- tests/test-bopomofo.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test-bopomofo.c b/tests/test-bopomofo.c index f708f44e3..b592ec946 100644 --- a/tests/test-bopomofo.c +++ b/tests/test-bopomofo.c @@ -1395,8 +1395,8 @@ void test_FuzzySearchMode_Hanyu() void test_SimpleEngine() { const TestData SIMPLE_INPUT[] = { - {"ru0320 5j4up ai6g4", "簡單住因模市" }, - {"ru0320 5j44up 2ai6g42", "簡單注音模式" }, + {"ru0320 5j4up ai6g4!", "簡單住因模市!" }, + {"ru0320 5j44up 2ai6g42!", "簡單注音模式!" }, }; size_t i; ChewingContext *ctx;