Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Merge fuzzy_search_mode and conversion_engine options #608

Merged
merged 2 commits into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 22 additions & 19 deletions capi/src/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@
};

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},
Expand All @@ -28,8 +27,9 @@
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;
Expand Down Expand Up @@ -310,7 +310,6 @@
| "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,
Expand Down Expand Up @@ -354,10 +353,10 @@
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,

Check warning on line 357 in capi/src/io.rs

View check run for this annotation

Codecov / codecov/patch

capi/src/io.rs#L357

Added line #L357 was not covered by tests
ConversionEngineKind::ChewingEngine => CHEWING_CONVERSION_ENGINE,
ConversionEngineKind::FuzzyChewingEngine => FUZZY_CHEWING_CONVERSION_ENGINE,

Check warning on line 359 in capi/src/io.rs

View check run for this annotation

Codecov / codecov/patch

capi/src/io.rs#L359

Added line #L359 was not covered by tests
},
"chewing.enable_fullwidth_toggle_key" => option.enable_fullwidth_toggle_key as c_int,
_ => ERROR,
Expand Down Expand Up @@ -448,21 +447,25 @@
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;

Check warning on line 461 in capi/src/io.rs

View check run for this annotation

Codecov / codecov/patch

capi/src/io.rs#L461

Added line #L461 was not covered by tests
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,
}
Expand Down Expand Up @@ -1138,13 +1141,13 @@
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,
}
}
Expand Down
6 changes: 6 additions & 0 deletions capi/src/public.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
15 changes: 15 additions & 0 deletions include/chewing.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down
72 changes: 8 additions & 64 deletions src/conversion/chewing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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<Item = Vec<Interval>> + 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<dyn Iterator<Item = Vec<Interval>> + 'a> {
Box::new(SimpleEngine::convert(self, dict, comp))
}
}

fn glue_fn(com: &Composition, mut acc: Vec<Interval>, interval: Interval) -> Vec<Interval> {
if acc.is_empty() {
acc.push(interval);
Expand Down Expand Up @@ -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.
Expand Down
29 changes: 29 additions & 0 deletions src/conversion/fuzzy.rs
Original file line number Diff line number Diff line change
@@ -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<dyn Iterator<Item = Vec<super::Interval>> + 'a> {
Box::new(ChewingEngine::convert(&self.inner, dict, comp))
}
}
6 changes: 5 additions & 1 deletion src/conversion/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! Algorithms to convert syllables to Chinese characters.

mod chewing;
mod fuzzy;
mod simple;
mod symbol;

use std::{
Expand All @@ -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 {
Expand Down
68 changes: 68 additions & 0 deletions src/conversion/simple.rs
Original file line number Diff line number Diff line change
@@ -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<Item = Vec<Interval>> + 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<dyn Iterator<Item = Vec<Interval>> + 'a> {
Box::new(SimpleEngine::convert(self, dict, comp))
}
}
Loading