Skip to content
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
16 changes: 14 additions & 2 deletions engine/crates/lex-session/src/auto_commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ impl InputSession {
}
// Extract data from comp() in a block so the borrow is dropped before
// we access self.history_records.
let (committed_reading, committed_surface, seg_pairs, commit_count) = {
let (committed_reading, committed_surface, committed_reading_len, seg_pairs, commit_count) = {
let c = self.comp();
if c.stability.count < 3 {
return None;
Expand Down Expand Up @@ -41,6 +41,17 @@ impl InputSession {
let segments: Vec<&ConvertedSegment> = best_path[0..commit_count].iter().collect();
let committed_reading: String = segments.iter().map(|s| s.reading.as_str()).collect();
let committed_surface: String = segments.iter().map(|s| s.surface.as_str()).collect();
let committed_reading_len = committed_reading.chars().count();

// Skip auto-commit if the committed reading is a single kana.
// Single-kana commits (e.g. "じ" from "じぇのさいど") destroy longer
// words that haven't been fully typed yet. Only applies to kana
// readings — ASCII prefixes (e.g. URLs) are not subject to this guard.
if lex_core::unicode::is_hiragana_reading(&committed_reading)
&& committed_reading_len < 2
{
return None;
}

if !c.kana.starts_with(&committed_reading) {
return None;
Expand All @@ -60,6 +71,7 @@ impl InputSession {
(
committed_reading,
committed_surface,
committed_reading_len,
seg_pairs,
commit_count,
)
Expand All @@ -78,7 +90,7 @@ impl InputSession {
// Safety: starts_with check above guarantees the byte offset is a valid
// UTF-8 boundary, but we use char-based slicing for extra safety.
let c = self.comp();
let skip_chars = committed_reading.chars().count();
let skip_chars = committed_reading_len;
c.kana = c.kana.chars().skip(skip_chars).collect();
c.stability.reset();

Expand Down
51 changes: 51 additions & 0 deletions engine/crates/lex-session/src/tests/candidates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,3 +240,54 @@ fn test_predictive_mode_no_auto_commit() {
);
}
}

#[test]
fn test_auto_commit_skips_single_kana_first_segment() {
use lex_core::candidates::generate_candidates;

let dict = make_test_dict();
let mut session = InputSession::new(dict.clone(), None, None);
session.set_defer_candidates(true);

fn complete_cycle(session: &mut InputSession, dict: &dyn Dictionary) -> Option<KeyResponse> {
let reading = session.comp().kana.clone();
if reading.is_empty() {
return None;
}
let cand = generate_candidates(dict, None, None, &reading, 20);
session.receive_candidates(&reading, cand.surfaces, cand.paths)
}
Comment thread
send marked this conversation as resolved.

// Type a reading where Viterbi may produce a single-kana first segment.
// Even if stability threshold is met, auto-commit must NOT fire when the
// committed reading is shorter than 2 kana.
type_string(&mut session, "ji");
complete_cycle(&mut session, &*dict);
type_string(&mut session, "ji");
complete_cycle(&mut session, &*dict);
type_string(&mut session, "ji");
complete_cycle(&mut session, &*dict);
type_string(&mut session, "ji");
let r = complete_cycle(&mut session, &*dict);

// Verify auto-commit preconditions are met (stability >= 3),
// so this test is actually exercising the min-length guard.
assert!(
session.comp().stability.count >= 3,
"stability count should reach threshold; got {}",
session.comp().stability.count
);

// Even after many cycles, a single-kana first segment must not auto-commit.
if let Some(ref resp) = r {
assert!(
resp.commit.is_none(),
"auto-commit should not commit when the first segment would be a single kana"
);
}
Comment thread
send marked this conversation as resolved.
Comment thread
send marked this conversation as resolved.
assert_eq!(
session.comp().kana,
"じじじじ",
"composing kana should remain unchanged when auto-commit is skipped"
);
}
Loading