Merge pull request #4 from vndangkhoa/devin/1782470733-fix-flush-spacing-v2
The auto-restore merge reintroduced the spacing bug: on every flush char the engine re-emitted a Replace and the daemon backspaced+retyped the already-on-screen composed word, racing against the separately-forwarded flush char and eating spaces (mất sự->mấtsự, đầu ngã xuống->đầungãxuống). Now flush only backspaces+retypes when auto-restore actually changes the word (English/invalid Vietnamese -> raw keystrokes). For a normal composed word the engine returns None and the daemon types only the flush char, leaving the correct word untouched on screen. Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: vndangkhoa <vonguyendangkhoa@gmail.com>
This commit is contained in:
commit
575de7a5a5
3 changed files with 32 additions and 31 deletions
|
|
@ -285,15 +285,17 @@ impl Daemon {
|
|||
fn replay_and_inject(&mut self, ch: char) -> Vec<OutputCommand> {
|
||||
let mut commands = Vec::new();
|
||||
|
||||
// Flush characters: commit current word, type the character, clear state.
|
||||
// The composed word is already correctly on screen, so we must NOT
|
||||
// backspace and retype it — doing so eats the spacing and shifts the
|
||||
// finished word left. Just type the flush char and clear state.
|
||||
// Flush characters: commit the current word and type the flush char.
|
||||
// Only backspace + retype when auto-restore actually CHANGES the word
|
||||
// (English / invalid Vietnamese). For a normal composed word it is
|
||||
// already correctly on screen, so retyping it would eat the spacing and
|
||||
// shift the finished word left.
|
||||
if is_flush_char(ch) {
|
||||
if !self.screen_output.is_empty() {
|
||||
let to_commit = self.word_to_commit();
|
||||
if !self.screen_output.is_empty() && to_commit != self.screen_output {
|
||||
let backspaces = self.screen_output.chars().count();
|
||||
commands.push(OutputCommand::Backspace(backspaces));
|
||||
commands.push(OutputCommand::Type(self.word_to_commit()));
|
||||
commands.push(OutputCommand::Type(to_commit));
|
||||
}
|
||||
// Type the flush character itself
|
||||
commands.push(OutputCommand::Type(ch.to_string()));
|
||||
|
|
@ -317,16 +319,15 @@ impl Daemon {
|
|||
);
|
||||
|
||||
if did_flush {
|
||||
// Engine flushed a word — it is already correctly on screen, so
|
||||
// just clear state without backspacing/retyping it (retyping eats
|
||||
// spacing and shifts the finished word left).
|
||||
// Engine flushed a word — commit it and clear state
|
||||
// The flush char (space/period/etc) was NOT in history, so we need to
|
||||
// type whatever was on screen + the flush char
|
||||
if !self.screen_output.is_empty() {
|
||||
// Engine flushed a word. Only backspace + retype when auto-restore
|
||||
// actually CHANGES the word; otherwise the composed word is already
|
||||
// correct on screen and retyping it eats spacing and shifts the
|
||||
// finished word left.
|
||||
let to_commit = self.word_to_commit();
|
||||
if !self.screen_output.is_empty() && to_commit != self.screen_output {
|
||||
let backspaces = self.screen_output.chars().count();
|
||||
commands.push(OutputCommand::Backspace(backspaces));
|
||||
commands.push(OutputCommand::Type(self.word_to_commit()));
|
||||
commands.push(OutputCommand::Type(to_commit));
|
||||
}
|
||||
self.keystroke_history.clear();
|
||||
self.screen_output.clear();
|
||||
|
|
|
|||
|
|
@ -213,25 +213,21 @@ impl Engine {
|
|||
|
||||
let raw = self.raw_buffer.clone();
|
||||
self.reset();
|
||||
// The composed word is already correctly on screen — re-typing it
|
||||
// here would trigger a redundant backspace + clipboard-paste cycle
|
||||
// that races against the separately-forwarded flush char, eating
|
||||
// spaces and merging words. Just finalize and let the flush char
|
||||
// through untouched.
|
||||
if prev_len > 0 {
|
||||
// Auto-restore: if the committed word is English / not valid
|
||||
// Vietnamese, revert to the raw keystrokes the user typed.
|
||||
// Vietnamese, revert to the raw keystrokes the user typed. This
|
||||
// genuinely changes the on-screen word, so a Replace is needed.
|
||||
if self.auto_restore && Engine::should_restore_word(&previous, &raw) {
|
||||
return Some(EngineEvent::Replace {
|
||||
backspaces: prev_len,
|
||||
insert: raw,
|
||||
});
|
||||
}
|
||||
// Don't include flush char in insert — daemon forwards it separately
|
||||
return Some(EngineEvent::Replace {
|
||||
backspaces: prev_len,
|
||||
insert: previous,
|
||||
});
|
||||
// Normal case: the composed word is already correctly on screen.
|
||||
// Re-typing it would trigger a redundant backspace + retype that
|
||||
// races against the separately-forwarded flush char, eating
|
||||
// spaces and merging words. Finalize and let the flush char
|
||||
// through untouched.
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,11 +91,15 @@ fn auto_restore_can_be_disabled() {
|
|||
for ch in "cargo".chars() {
|
||||
engine.process_key(ch);
|
||||
}
|
||||
// With auto-restore off the Vietnamese composition is kept on screen
|
||||
// (no restore back to the raw English keystrokes).
|
||||
assert_eq!(engine.buffer(), "cảgo");
|
||||
// The composed word is already correct on screen, so flushing emits no
|
||||
// Replace — re-typing it would race with the forwarded flush char and eat
|
||||
// the spacing. (Contrast with auto-restore on, which emits Replace→"cargo".)
|
||||
let event = engine.process_key(' ');
|
||||
match event {
|
||||
Some(vietc_engine::EngineEvent::Replace { insert, .. }) => {
|
||||
assert_eq!(insert, "cảgo", "with auto-restore off the VN form is kept");
|
||||
}
|
||||
other => panic!("expected Replace to 'cảgo', got {other:?}"),
|
||||
}
|
||||
assert!(
|
||||
event.is_none(),
|
||||
"with auto-restore off the composed VN word stays untouched on flush, got {event:?}"
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue