From 1010cf79322fb92f85c3dba1f6ee33bf1419acf0 Mon Sep 17 00:00:00 2001 From: H-Chris233 Date: Sat, 6 Jun 2026 21:41:03 +0800 Subject: [PATCH] fix(linux): restore clipboard fallback after fcitx failure --- openless-all/app/src-tauri/src/insertion.rs | 129 +++++++++++++++++--- 1 file changed, 115 insertions(+), 14 deletions(-) diff --git a/openless-all/app/src-tauri/src/insertion.rs b/openless-all/app/src-tauri/src/insertion.rs index d7876600d..d25020210 100644 --- a/openless-all/app/src-tauri/src/insertion.rs +++ b/openless-all/app/src-tauri/src/insertion.rs @@ -30,24 +30,21 @@ impl TextInserter { Self } - /// Linux 路径:仅走 fcitx5 CommitText 直写。无剪贴板 fallback。 + /// Linux 路径:优先走 fcitx5 CommitText;插件不可用或提交失败时回退剪贴板粘贴。 #[cfg(target_os = "linux")] pub fn insert( &self, text: &str, - _restore_clipboard_after_paste: bool, - _paste_shortcut: PasteShortcut, + restore_clipboard_after_paste: bool, + paste_shortcut: PasteShortcut, ) -> InsertStatus { - if text.is_empty() { - return InsertStatus::CopiedFallback; - } - match crate::linux_fcitx::commit_text(text) { - Ok(()) => InsertStatus::Inserted, - Err(e) => { - log::warn!("[insertion] fcitx commit_text failed: {e}"); - InsertStatus::Failed - } - } + insert_with_fcitx_or_clipboard_fallback( + text, + restore_clipboard_after_paste, + paste_shortcut, + crate::linux_fcitx::commit_text, + insert_with_clipboard_restore, + ) } /// Windows 路径:写剪贴板 + 模拟 `paste_shortcut`。 @@ -73,7 +70,10 @@ impl TextInserter { restore_clipboard_after_paste: bool, paste_shortcut: PasteShortcut, ) -> InsertStatus { - self.insert(text, restore_clipboard_after_paste, paste_shortcut) + if text.is_empty() { + return InsertStatus::CopiedFallback; + } + insert_with_clipboard_restore(text, restore_clipboard_after_paste, paste_shortcut) } #[cfg(target_os = "windows")] @@ -120,6 +120,32 @@ impl TextInserter { } } +#[cfg(target_os = "linux")] +fn insert_with_fcitx_or_clipboard_fallback( + text: &str, + restore_clipboard_after_paste: bool, + paste_shortcut: PasteShortcut, + commit_text: C, + clipboard_fallback: F, +) -> InsertStatus +where + C: FnOnce(&str) -> Result<(), String>, + F: FnOnce(&str, bool, PasteShortcut) -> InsertStatus, +{ + if text.is_empty() { + return InsertStatus::CopiedFallback; + } + match commit_text(text) { + Ok(()) => InsertStatus::Inserted, + Err(err) => { + log::warn!( + "[insertion] fcitx commit_text failed, falling back to clipboard paste: {err}" + ); + clipboard_fallback(text, restore_clipboard_after_paste, paste_shortcut) + } + } +} + #[cfg(target_os = "macos")] fn macos_insert_status_after_paste(result: Result<(), String>) -> InsertStatus { match result { @@ -589,6 +615,81 @@ mod tests { assert_eq!(inserter.copy_fallback(""), InsertStatus::CopiedFallback); } + #[test] + #[cfg(target_os = "linux")] + fn linux_commit_text_success_skips_clipboard_fallback() { + let mut fallback_called = false; + + let status = insert_with_fcitx_or_clipboard_fallback( + "dictated text", + true, + PasteShortcut::CtrlV, + |text| { + assert_eq!(text, "dictated text"); + Ok(()) + }, + |_, _, _| { + fallback_called = true; + InsertStatus::CopiedFallback + }, + ); + + assert_eq!(status, InsertStatus::Inserted); + assert!(!fallback_called); + } + + #[test] + #[cfg(target_os = "linux")] + fn linux_commit_text_failure_uses_clipboard_fallback() { + let mut fallback_args = None; + + let status = insert_with_fcitx_or_clipboard_fallback( + "dictated text", + true, + PasteShortcut::CtrlShiftV, + |_| Err("plugin unavailable".to_string()), + |text, restore_clipboard_after_paste, paste_shortcut| { + fallback_args = Some(( + text.to_string(), + restore_clipboard_after_paste, + paste_shortcut, + )); + InsertStatus::CopiedFallback + }, + ); + + assert_eq!(status, InsertStatus::CopiedFallback); + assert_eq!( + fallback_args, + Some(("dictated text".to_string(), true, PasteShortcut::CtrlShiftV)) + ); + } + + #[test] + #[cfg(target_os = "linux")] + fn linux_empty_insert_skips_commit_text_and_clipboard_fallback() { + let mut commit_called = false; + let mut fallback_called = false; + + let status = insert_with_fcitx_or_clipboard_fallback( + "", + true, + PasteShortcut::CtrlV, + |_| { + commit_called = true; + Ok(()) + }, + |_, _, _| { + fallback_called = true; + InsertStatus::CopiedFallback + }, + ); + + assert_eq!(status, InsertStatus::CopiedFallback); + assert!(!commit_called); + assert!(!fallback_called); + } + #[test] #[cfg(not(target_os = "macos"))] fn pending_clipboard_restore_keeps_first_original_until_latest_restore() {