Skip to content

Commit 6b7ea21

Browse files
authored
Merge pull request #213 from send/fix/auto-commit-min-reading-length
fix: skip auto-commit when committed reading is shorter than 2 kana
2 parents 88a41d5 + b88f205 commit 6b7ea21

2 files changed

Lines changed: 65 additions & 2 deletions

File tree

engine/crates/lex-session/src/auto_commit.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ impl InputSession {
1212
}
1313
// Extract data from comp() in a block so the borrow is dropped before
1414
// we access self.history_records.
15-
let (committed_reading, committed_surface, seg_pairs, commit_count) = {
15+
let (committed_reading, committed_surface, committed_reading_len, seg_pairs, commit_count) = {
1616
let c = self.comp();
1717
if c.stability.count < 3 {
1818
return None;
@@ -41,6 +41,17 @@ impl InputSession {
4141
let segments: Vec<&ConvertedSegment> = best_path[0..commit_count].iter().collect();
4242
let committed_reading: String = segments.iter().map(|s| s.reading.as_str()).collect();
4343
let committed_surface: String = segments.iter().map(|s| s.surface.as_str()).collect();
44+
let committed_reading_len = committed_reading.chars().count();
45+
46+
// Skip auto-commit if the committed reading is a single kana.
47+
// Single-kana commits (e.g. "じ" from "じぇのさいど") destroy longer
48+
// words that haven't been fully typed yet. Only applies to kana
49+
// readings — ASCII prefixes (e.g. URLs) are not subject to this guard.
50+
if lex_core::unicode::is_hiragana_reading(&committed_reading)
51+
&& committed_reading_len < 2
52+
{
53+
return None;
54+
}
4455

4556
if !c.kana.starts_with(&committed_reading) {
4657
return None;
@@ -60,6 +71,7 @@ impl InputSession {
6071
(
6172
committed_reading,
6273
committed_surface,
74+
committed_reading_len,
6375
seg_pairs,
6476
commit_count,
6577
)
@@ -78,7 +90,7 @@ impl InputSession {
7890
// Safety: starts_with check above guarantees the byte offset is a valid
7991
// UTF-8 boundary, but we use char-based slicing for extra safety.
8092
let c = self.comp();
81-
let skip_chars = committed_reading.chars().count();
93+
let skip_chars = committed_reading_len;
8294
c.kana = c.kana.chars().skip(skip_chars).collect();
8395
c.stability.reset();
8496

engine/crates/lex-session/src/tests/candidates.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,3 +240,54 @@ fn test_predictive_mode_no_auto_commit() {
240240
);
241241
}
242242
}
243+
244+
#[test]
245+
fn test_auto_commit_skips_single_kana_first_segment() {
246+
use lex_core::candidates::generate_candidates;
247+
248+
let dict = make_test_dict();
249+
let mut session = InputSession::new(dict.clone(), None, None);
250+
session.set_defer_candidates(true);
251+
252+
fn complete_cycle(session: &mut InputSession, dict: &dyn Dictionary) -> Option<KeyResponse> {
253+
let reading = session.comp().kana.clone();
254+
if reading.is_empty() {
255+
return None;
256+
}
257+
let cand = generate_candidates(dict, None, None, &reading, 20);
258+
session.receive_candidates(&reading, cand.surfaces, cand.paths)
259+
}
260+
261+
// Type a reading where Viterbi may produce a single-kana first segment.
262+
// Even if stability threshold is met, auto-commit must NOT fire when the
263+
// committed reading is shorter than 2 kana.
264+
type_string(&mut session, "ji");
265+
complete_cycle(&mut session, &*dict);
266+
type_string(&mut session, "ji");
267+
complete_cycle(&mut session, &*dict);
268+
type_string(&mut session, "ji");
269+
complete_cycle(&mut session, &*dict);
270+
type_string(&mut session, "ji");
271+
let r = complete_cycle(&mut session, &*dict);
272+
273+
// Verify auto-commit preconditions are met (stability >= 3),
274+
// so this test is actually exercising the min-length guard.
275+
assert!(
276+
session.comp().stability.count >= 3,
277+
"stability count should reach threshold; got {}",
278+
session.comp().stability.count
279+
);
280+
281+
// Even after many cycles, a single-kana first segment must not auto-commit.
282+
if let Some(ref resp) = r {
283+
assert!(
284+
resp.commit.is_none(),
285+
"auto-commit should not commit when the first segment would be a single kana"
286+
);
287+
}
288+
assert_eq!(
289+
session.comp().kana,
290+
"じじじじ",
291+
"composing kana should remain unchanged when auto-commit is skipped"
292+
);
293+
}

0 commit comments

Comments
 (0)