Skip to content

Commit b98b8d5

Browse files
committed
feat(core): aggressive space auto-commit and syntactic casing restoration
Implements automatic capitalization after punctuation symbols and triggers an auto-commit of predictions if there is exactly 1 valid candidate and space is pressed, drastically reducing keystrokes.
1 parent d731dd8 commit b98b8d5

2 files changed

Lines changed: 37 additions & 1 deletion

File tree

crates/smartkey-core/src/caps.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,16 @@ impl CapsEngine {
2828
}
2929
}
3030

31+
/// Determine if word requires capitalization based on the previous token.
32+
pub fn requires_capitalization(prev_token: &str) -> bool {
33+
let trimmed = prev_token.trim();
34+
if trimmed.is_empty() {
35+
return true; // Start of input
36+
}
37+
let last_char = trimmed.chars().last().unwrap_or(' ');
38+
matches!(last_char, '.' | '!' | '?' | '\n')
39+
}
40+
3141
/// Detect caps regime from the typed prefix.
3242
///
3343
/// Returns `Normal` for empty strings, all-lowercase, or single uppercase

crates/smartkey-core/src/input.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,26 @@ impl InputMethodCore {
569569

570570
// Space / Return: commit current word, delimit.
571571
Key::Space | Key::Return => {
572+
let is_space = matches!(key, Key::Space);
573+
574+
// Aggressive Space-Commit (Goal 3): Auto-commit unique top prediction on Space.
575+
if is_space && !self.current_word.is_empty() && self.last_predictions.len() == 1 {
576+
let best_match = self.last_predictions[0].word.clone();
577+
// Don't auto-commit if they typed the exact word already
578+
if best_match.to_lowercase() != self.current_word.to_lowercase() {
579+
let mut actions = vec![Action::HideGhost];
580+
let typed_len = self.current_word.chars().count();
581+
self.commit_word_internal(&best_match, true);
582+
self.reset_word();
583+
actions.push(Action::ReplaceWord {
584+
len: typed_len,
585+
text: best_match,
586+
});
587+
actions.push(Action::ForwardKey);
588+
return actions;
589+
}
590+
}
591+
572592
if let Some(ref db) = self.dual_buffer {
573593
if !db.is_locked() && !self.current_word.is_empty() {
574594
// Hypothesis phase: commit preedit prefix before forwarding.
@@ -1041,9 +1061,15 @@ impl InputMethodCore {
10411061
.map(|c| c.is_uppercase())
10421062
.unwrap_or(false);
10431063

1064+
let prev_token = ctx_refs.last().copied().unwrap_or("");
1065+
let force_sentence_start = CapsEngine::requires_capitalization(prev_token);
1066+
10441067
for pred in &mut self.last_predictions {
10451068
pred.word = CapsEngine::apply_caps(&pred.word, regime);
1046-
// For Normal regime with uppercase first char (sentence-start, proper noun).
1069+
// For Normal regime with uppercase first char (sentence-start, proper noun) or lazy typing.
1070+
if regime == CapsRegime::Normal && (first_char_upper || force_sentence_start) {
1071+
pred.word = CapsEngine::capitalize_first(&pred.word);
1072+
}
10471073
if regime == CapsRegime::Normal && first_char_upper {
10481074
pred.word = CapsEngine::capitalize_first(&pred.word);
10491075
}

0 commit comments

Comments
 (0)