Skip to content

Commit a110f54

Browse files
committed
feat(dict): introduce the usage property
1 parent fdfb78a commit a110f54

File tree

20 files changed

+411
-186
lines changed

20 files changed

+411
-186
lines changed

.helix/languages.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[language-server.rust-analyzer.config]
2+
cargo.features = ["sqlite"]

NEWS

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
What's New in libchewing (unreleased)
22
---------------------------------------------------------
33

4+
* Features
5+
- dict: loading user dictionary are now also controlled by enabled_dicts
6+
in `chewing_new3()`.
7+
8+
* Bug Fixes
9+
- dict: fixed parsing trie dictionary file with extension fields.
10+
411
* Changes
12+
- rust: breaking! renamed SystemDictionaryLoader to AssetLoader.
13+
- rust: breaking! renamed UserDictionaryLoader to UserDictionaryManager.
14+
- rust: Dictionary trait gained a new `set_usage()` method.
515
- conversion: adjust max output paths down to 10.
616

717
What's New in libchewing 0.11.0 (January 10, 2026)

capi/src/io.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ pub unsafe extern "C" fn chewing_new2(
134134
chewing_new3(
135135
syspath,
136136
userpath,
137-
c"word.dat,tsi.dat".as_ptr(),
137+
c"word.dat,tsi.dat,chewing.dat".as_ptr(),
138138
logger,
139139
loggerdata,
140140
)
@@ -237,7 +237,7 @@ pub unsafe extern "C" fn chewing_new3(
237237
/// don't need to be freed.
238238
#[unsafe(no_mangle)]
239239
pub unsafe extern "C" fn chewing_get_defaultDictionaryNames() -> *const c_char {
240-
c"word.dat,tsi.dat".as_ptr()
240+
c"word.dat,tsi.dat,chewing.dat".as_ptr()
241241
}
242242

243243
/// Releases the resources used by the given Chewing IM instance.

src/dictionary/layered.rs

Lines changed: 60 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
use std::{
2-
collections::{BTreeMap, btree_map::Entry},
3-
iter,
4-
};
1+
use std::collections::{BTreeMap, btree_map::Entry};
52

63
use log::error;
74

85
use super::{Dictionary, DictionaryInfo, Entries, LookupStrategy, Phrase, UpdateDictionaryError};
9-
use crate::zhuyin::Syllable;
6+
use crate::{
7+
dictionary::{DictionaryUsage, TrieBuf},
8+
zhuyin::Syllable,
9+
};
1010

1111
/// A collection of dictionaries that returns the union of the lookup results.
1212
/// # Examples
@@ -29,7 +29,7 @@ use crate::zhuyin::Syllable;
2929
/// vec![("策", 100), ("冊", 100)]
3030
/// )]);
3131
///
32-
/// let dict = Layered::new(vec![Box::new(sys_dict)], Box::new(user_dict));
32+
/// let dict = Layered::new(vec![Box::new(sys_dict), Box::new(user_dict)]);
3333
/// assert_eq!(
3434
/// [
3535
/// ("側", 1, 0).into(),
@@ -48,20 +48,31 @@ use crate::zhuyin::Syllable;
4848
/// ```
4949
#[derive(Debug)]
5050
pub struct Layered {
51-
sys_dict: Vec<Box<dyn Dictionary>>,
52-
user_dict: Box<dyn Dictionary>,
51+
dicts: Vec<Box<dyn Dictionary>>,
52+
user_dict_index: usize,
5353
}
5454

5555
impl Layered {
5656
/// Creates a new `Layered` with the list of dictionaries.
57-
pub fn new(sys_dict: Vec<Box<dyn Dictionary>>, user_dict: Box<dyn Dictionary>) -> Layered {
57+
pub fn new(mut dicts: Vec<Box<dyn Dictionary>>) -> Layered {
58+
let user_dict_index = dicts.iter().enumerate().find_map(|d| {
59+
if d.1.about().usage == DictionaryUsage::User {
60+
Some(d.0)
61+
} else {
62+
None
63+
}
64+
});
65+
if user_dict_index.is_none() {
66+
dicts.push(Box::new(TrieBuf::new_in_memory()));
67+
}
68+
let user_dict_index = user_dict_index.unwrap_or(dicts.len() - 1);
5869
Layered {
59-
sys_dict,
60-
user_dict,
70+
dicts,
71+
user_dict_index,
6172
}
6273
}
6374
pub fn user_dict(&mut self) -> &mut dyn Dictionary {
64-
self.user_dict.as_mut()
75+
self.dicts[self.user_dict_index].as_mut()
6576
}
6677
}
6778

@@ -89,44 +100,36 @@ impl Dictionary for Layered {
89100
let mut sort_map: BTreeMap<String, usize> = BTreeMap::new();
90101
let mut phrases: Vec<Phrase> = Vec::new();
91102

92-
self.sys_dict
93-
.iter()
94-
.chain(iter::once(&self.user_dict))
95-
.for_each(|d| {
96-
for phrase in d.lookup(syllables, strategy) {
97-
debug_assert!(!phrase.as_str().is_empty());
98-
match sort_map.entry(phrase.to_string()) {
99-
Entry::Occupied(entry) => {
100-
let index = *entry.get();
101-
phrases[index].freq += phrase.freq;
102-
phrases[index].last_used =
103-
match (phrases[index].last_used, phrase.last_used) {
104-
(Some(orig), Some(new)) => Some(u64::max(orig, new)),
105-
(Some(orig), None) => Some(orig),
106-
(None, Some(new)) => Some(new),
107-
(None, None) => None,
108-
};
109-
}
110-
Entry::Vacant(entry) => {
111-
entry.insert(phrases.len());
112-
phrases.push(phrase);
113-
}
103+
self.dicts.iter().for_each(|d| {
104+
for phrase in d.lookup(syllables, strategy) {
105+
debug_assert!(!phrase.as_str().is_empty());
106+
match sort_map.entry(phrase.to_string()) {
107+
Entry::Occupied(entry) => {
108+
let index = *entry.get();
109+
phrases[index].freq += phrase.freq;
110+
phrases[index].last_used =
111+
match (phrases[index].last_used, phrase.last_used) {
112+
(Some(orig), Some(new)) => Some(u64::max(orig, new)),
113+
(Some(orig), None) => Some(orig),
114+
(None, Some(new)) => Some(new),
115+
(None, None) => None,
116+
};
117+
}
118+
Entry::Vacant(entry) => {
119+
entry.insert(phrases.len());
120+
phrases.push(phrase);
114121
}
115122
}
116-
});
123+
}
124+
});
117125
phrases
118126
}
119127

120128
/// Returns all entries from all dictionaries.
121129
///
122130
/// **NOTE**: Duplicate entries are not removed.
123131
fn entries(&self) -> Entries<'_> {
124-
Box::new(
125-
self.sys_dict
126-
.iter()
127-
.chain(iter::once(&self.user_dict))
128-
.flat_map(|dict| dict.entries()),
129-
)
132+
Box::new(self.dicts.iter().flat_map(|dict| dict.entries()))
130133
}
131134

132135
fn about(&self) -> DictionaryInfo {
@@ -140,12 +143,14 @@ impl Dictionary for Layered {
140143
None
141144
}
142145

146+
fn set_usage(&mut self, _usage: DictionaryUsage) {}
147+
143148
fn reopen(&mut self) -> Result<(), UpdateDictionaryError> {
144-
self.user_dict.reopen()
149+
self.user_dict().reopen()
145150
}
146151

147152
fn flush(&mut self) -> Result<(), UpdateDictionaryError> {
148-
self.user_dict.flush()
153+
self.user_dict().flush()
149154
}
150155

151156
fn add_phrase(
@@ -157,7 +162,7 @@ impl Dictionary for Layered {
157162
error!("BUG! added phrase is empty");
158163
return Ok(());
159164
}
160-
self.user_dict.add_phrase(syllables, phrase)
165+
self.user_dict().add_phrase(syllables, phrase)
161166
}
162167

163168
fn update_phrase(
@@ -171,7 +176,7 @@ impl Dictionary for Layered {
171176
error!("BUG! added phrase is empty");
172177
return Ok(());
173178
}
174-
self.user_dict
179+
self.user_dict()
175180
.update_phrase(syllables, phrase, user_freq, time)
176181
}
177182

@@ -180,7 +185,8 @@ impl Dictionary for Layered {
180185
syllables: &[Syllable],
181186
phrase_str: &str,
182187
) -> Result<(), UpdateDictionaryError> {
183-
self.user_dict.remove_phrase(syllables, phrase_str)
188+
// TODO use exclude list
189+
self.user_dict().remove_phrase(syllables, phrase_str)
184190
}
185191
}
186192

@@ -194,7 +200,8 @@ mod tests {
194200
use super::Layered;
195201
use crate::{
196202
dictionary::{
197-
Dictionary, DictionaryBuilder, LookupStrategy, Phrase, Trie, TrieBuf, TrieBuilder,
203+
Dictionary, DictionaryBuilder, DictionaryUsage, LookupStrategy, Phrase, Trie, TrieBuf,
204+
TrieBuilder,
198205
},
199206
syl,
200207
zhuyin::Bopomofo,
@@ -211,7 +218,7 @@ mod tests {
211218
vec![("策", 100), ("冊", 100)],
212219
)]);
213220

214-
let dict = Layered::new(vec![Box::new(sys_dict)], Box::new(user_dict));
221+
let dict = Layered::new(vec![Box::new(sys_dict), Box::new(user_dict)]);
215222
assert_eq!(
216223
[
217224
(
@@ -253,7 +260,7 @@ mod tests {
253260
vec![("策", 100), ("冊", 100)],
254261
)]);
255262

256-
let dict = Layered::new(vec![Box::new(sys_dict)], Box::new(user_dict));
263+
let dict = Layered::new(vec![Box::new(sys_dict), Box::new(user_dict)]);
257264
assert_eq!(
258265
Some(("側", 1, 0).into()),
259266
dict.lookup(
@@ -287,6 +294,7 @@ mod tests {
287294
vec![("測", 1), ("冊", 1), ("側", 1)],
288295
)]);
289296
let mut builder = TrieBuilder::new();
297+
builder.set_usage(DictionaryUsage::User);
290298
builder.insert(
291299
&[syl![Bopomofo::C, Bopomofo::E, Bopomofo::TONE4]],
292300
("策", 100, 0).into(),
@@ -298,9 +306,10 @@ mod tests {
298306
let mut cursor = Cursor::new(vec![]);
299307
builder.write(&mut cursor)?;
300308
cursor.rewind()?;
301-
let user_dict = Trie::new(&mut cursor)?;
309+
let mut user_dict = Trie::new(&mut cursor)?;
310+
user_dict.set_usage(DictionaryUsage::User);
302311

303-
let mut dict = Layered::new(vec![Box::new(sys_dict)], Box::new(user_dict));
312+
let mut dict = Layered::new(vec![Box::new(sys_dict), Box::new(user_dict)]);
304313
assert_eq!(
305314
Some(("側", 1, 0).into()),
306315
dict.lookup(

0 commit comments

Comments
 (0)