chewing/editor/zhuyin_layout/
hsu.rs1use crate::{
4 editor::keyboard::KeyCode,
5 syl,
6 zhuyin::{Bopomofo, BopomofoKind, Syllable},
7};
8
9use super::{KeyBehavior, KeyEvent, SyllableEditor};
10
11#[derive(Debug, Clone, Copy)]
13pub struct Hsu {
14 syllable: Syllable,
15}
16
17impl Hsu {
18 pub fn new() -> Hsu {
20 Hsu {
21 syllable: Default::default(),
22 }
23 }
24
25 fn is_hsu_end_key(&self, key: KeyEvent) -> bool {
34 match key.code {
36 KeyCode::S | KeyCode::D | KeyCode::F | KeyCode::J | KeyCode::Space => {
37 !self.syllable.is_empty()
38 }
39 _ => false,
40 }
41 }
42 fn has_initial_or_medial(&self) -> bool {
43 self.syllable.has_initial() || self.syllable.has_medial()
44 }
45
46 const ALT_TABLE: &'static [(Syllable, &'static [Syllable])] = &[
47 (syl![Bopomofo::C], &[syl![Bopomofo::EI]]),
48 (syl![Bopomofo::I], &[syl![Bopomofo::EH]]),
49 (syl![Bopomofo::S], &[syl![Bopomofo::TONE5]]),
50 (syl![Bopomofo::D], &[syl![Bopomofo::TONE2]]),
51 (syl![Bopomofo::F], &[syl![Bopomofo::TONE3]]),
52 (syl![Bopomofo::E], &[syl![Bopomofo::G]]),
53 (syl![Bopomofo::O], &[syl![Bopomofo::H]]),
54 (
55 syl![Bopomofo::ZH],
56 &[syl![Bopomofo::J], syl![Bopomofo::TONE4]],
57 ),
58 (syl![Bopomofo::ANG], &[syl![Bopomofo::K]]),
59 (
60 syl![Bopomofo::ER],
61 &[syl![Bopomofo::L], syl![Bopomofo::ENG]],
62 ),
63 (syl![Bopomofo::SH], &[syl![Bopomofo::X]]),
64 (syl![Bopomofo::CH], &[syl![Bopomofo::Q]]),
65 (syl![Bopomofo::EN], &[syl![Bopomofo::N]]),
66 (syl![Bopomofo::AN], &[syl![Bopomofo::M]]),
67 ];
68}
69
70impl Default for Hsu {
71 fn default() -> Self {
72 Self::new()
73 }
74}
75
76impl SyllableEditor for Hsu {
77 fn key_press(&mut self, key: KeyEvent) -> KeyBehavior {
78 if self.is_hsu_end_key(key) {
79 if !self.syllable.has_medial() && !self.syllable.has_rime() {
80 if let Some(key) = self.syllable.initial() {
81 match key {
82 Bopomofo::J => {
83 self.syllable.update(Bopomofo::ZH);
84 }
85 Bopomofo::Q => {
86 self.syllable.update(Bopomofo::CH);
87 }
88 Bopomofo::X => {
89 self.syllable.update(Bopomofo::SH);
90 }
91 Bopomofo::H => {
92 self.syllable.remove_initial();
93 self.syllable.update(Bopomofo::O);
94 }
95 Bopomofo::G => {
96 self.syllable.remove_initial();
97 self.syllable.update(Bopomofo::E);
98 }
99 Bopomofo::M => {
100 self.syllable.remove_initial();
101 self.syllable.update(Bopomofo::AN);
102 }
103 Bopomofo::N => {
104 self.syllable.remove_initial();
105 self.syllable.update(Bopomofo::EN);
106 }
107 Bopomofo::K => {
108 self.syllable.remove_initial();
109 self.syllable.update(Bopomofo::ANG);
110 }
111 Bopomofo::L => {
112 self.syllable.remove_initial();
113 self.syllable.update(Bopomofo::ER);
114 }
115 _ => (),
116 }
117 }
118 }
119
120 match (self.syllable.initial(), self.syllable.medial()) {
122 (Some(Bopomofo::G), Some(Bopomofo::I))
123 | (Some(Bopomofo::G), Some(Bopomofo::IU)) => {
124 self.syllable.update(Bopomofo::J);
125 }
126 _ => (),
127 }
128
129 match key.code {
130 KeyCode::D => self.syllable.update(Bopomofo::TONE2),
132 KeyCode::F => self.syllable.update(Bopomofo::TONE3),
133 KeyCode::J => self.syllable.update(Bopomofo::TONE4),
134 KeyCode::S => self.syllable.update(Bopomofo::TONE5),
135 _ => {
136 self.syllable.remove_tone();
137 }
138 };
139 KeyBehavior::Commit
140 } else {
141 let bopomofo = match key.code {
142 KeyCode::A => {
143 if self.has_initial_or_medial() {
144 Bopomofo::EI
145 } else {
146 Bopomofo::C
147 }
148 }
149 KeyCode::B => Bopomofo::B,
150 KeyCode::C => Bopomofo::SH,
151 KeyCode::D => Bopomofo::D,
152 KeyCode::E => {
153 if self.syllable.has_medial() {
154 Bopomofo::EH
155 } else {
156 Bopomofo::I
157 }
158 }
159 KeyCode::F => Bopomofo::F,
160 KeyCode::G => {
161 if self.has_initial_or_medial() {
162 Bopomofo::E
163 } else {
164 Bopomofo::G
165 }
166 }
167 KeyCode::H => {
168 if self.has_initial_or_medial() {
169 Bopomofo::O
170 } else {
171 Bopomofo::H
172 }
173 }
174 KeyCode::I => Bopomofo::AI,
175 KeyCode::J => Bopomofo::ZH,
176 KeyCode::K => {
177 if self.has_initial_or_medial() {
178 Bopomofo::ANG
179 } else {
180 Bopomofo::K
181 }
182 }
183 KeyCode::L => {
184 if self.has_initial_or_medial() {
185 Bopomofo::ENG
186 } else {
187 Bopomofo::L
188 }
189 }
190 KeyCode::M => {
191 if self.has_initial_or_medial() {
192 Bopomofo::AN
193 } else {
194 Bopomofo::M
195 }
196 }
197 KeyCode::N => {
198 if self.has_initial_or_medial() {
199 Bopomofo::EN
200 } else {
201 Bopomofo::N
202 }
203 }
204 KeyCode::O => Bopomofo::OU,
205 KeyCode::P => Bopomofo::P,
206 KeyCode::R => Bopomofo::R,
207 KeyCode::S => Bopomofo::S,
208 KeyCode::T => Bopomofo::T,
209 KeyCode::U => Bopomofo::IU,
210 KeyCode::V => Bopomofo::CH,
211 KeyCode::W => Bopomofo::AU,
212 KeyCode::X => Bopomofo::U,
213 KeyCode::Y => Bopomofo::A,
214 KeyCode::Z => Bopomofo::Z,
215 _ => return KeyBehavior::NoWord,
216 };
217 let kind = bopomofo.kind();
218
219 match (self.syllable.initial(), self.syllable.medial()) {
221 (Some(Bopomofo::G), Some(Bopomofo::I))
222 | (Some(Bopomofo::G), Some(Bopomofo::IU)) => {
223 self.syllable.update(Bopomofo::J);
224 }
225 _ => (),
226 }
227
228 if (kind == BopomofoKind::Medial && bopomofo == Bopomofo::U)
230 || (kind == BopomofoKind::Rime && self.syllable.medial().is_none())
231 {
232 match self.syllable.initial() {
233 Some(Bopomofo::J) => {
234 self.syllable.update(Bopomofo::ZH);
235 }
236 Some(Bopomofo::Q) => {
237 self.syllable.update(Bopomofo::CH);
238 }
239 Some(Bopomofo::X) => {
240 self.syllable.update(Bopomofo::SH);
241 }
242 _ => (),
243 }
244 }
245
246 if bopomofo == Bopomofo::I || bopomofo == Bopomofo::IU {
248 match self.syllable.initial() {
249 Some(Bopomofo::ZH) => {
250 self.syllable.update(Bopomofo::J);
251 }
252 Some(Bopomofo::CH) => {
253 self.syllable.update(Bopomofo::Q);
254 }
255 Some(Bopomofo::SH) => {
256 self.syllable.update(Bopomofo::X);
257 }
258 _ => (),
259 }
260 }
261
262 self.syllable.update(bopomofo);
263
264 KeyBehavior::Absorb
265 }
266 }
267
268 fn is_empty(&self) -> bool {
269 self.syllable.is_empty()
270 }
271
272 fn remove_last(&mut self) {
273 self.syllable.pop();
274 }
275
276 fn clear(&mut self) {
277 self.syllable.clear();
278 }
279
280 fn read(&self) -> Syllable {
281 self.syllable
282 }
283
284 fn alt_syllables(&self, syl: Syllable) -> &[Syllable] {
285 for entry in Self::ALT_TABLE {
286 if entry.0 == syl {
287 return entry.1;
288 }
289 }
290 &[]
291 }
292
293 fn clone(&self) -> Box<dyn SyllableEditor> {
294 Box::new(Clone::clone(self))
295 }
296}
297
298#[cfg(test)]
299mod test {
300
301 use crate::{
302 editor::{
303 keyboard::{KeyCode, KeyboardLayout, Qwerty},
304 zhuyin_layout::SyllableEditor,
305 },
306 zhuyin::Bopomofo,
307 };
308
309 use super::Hsu;
310
311 #[test]
312 fn cen() {
313 let mut hsu = Hsu::new();
314 let keyboard = Qwerty;
315 hsu.key_press(keyboard.map(KeyCode::C));
316 hsu.key_press(keyboard.map(KeyCode::E));
317 hsu.key_press(keyboard.map(KeyCode::N));
318 hsu.key_press(keyboard.map(KeyCode::Space));
319 let result = hsu.read();
320 assert_eq!(result.initial(), Some(Bopomofo::X));
321 assert_eq!(result.medial(), Some(Bopomofo::I));
322 assert_eq!(result.rime(), Some(Bopomofo::EN));
323 }
324
325 #[test]
326 fn convert_n_to_en() {
327 let mut hsu = Hsu::new();
328 let keyboard = Qwerty;
329 hsu.key_press(keyboard.map(KeyCode::N));
330 hsu.key_press(keyboard.map(KeyCode::F));
331 let result = hsu.read();
332 assert_eq!(result.rime(), Some(Bopomofo::EN));
333 }
334}