diff --git a/crates/tui/src/tui/keybindings.rs b/crates/tui/src/tui/keybindings.rs index 7a305cb0..b38de8b6 100644 --- a/crates/tui/src/tui/keybindings.rs +++ b/crates/tui/src/tui/keybindings.rs @@ -145,7 +145,7 @@ pub const KEYBINDINGS: &[KeybindingEntry] = &[ section: KeybindingSection::Editing, }, KeybindingEntry { - chord: "Ctrl+J / Alt+Enter", + chord: "Ctrl+J / Alt+Enter / Shift+Enter", description_id: crate::localization::MessageId::KbInsertNewline, section: KeybindingSection::Editing, }, diff --git a/crates/tui/src/tui/ui.rs b/crates/tui/src/tui/ui.rs index 88e25cdb..e5d500b9 100644 --- a/crates/tui/src/tui/ui.rs +++ b/crates/tui/src/tui/ui.rs @@ -2370,10 +2370,7 @@ async fn run_event_loop( continue; } // Input handling - KeyCode::Char('j') if key.modifiers.contains(KeyModifiers::CONTROL) => { - app.insert_char('\n'); - } - KeyCode::Enter if key.modifiers.contains(KeyModifiers::ALT) => { + _ if is_composer_newline_key(key) => { app.insert_char('\n'); } KeyCode::Enter @@ -3108,6 +3105,18 @@ fn next_escape_action(app: &App, slash_menu_open: bool) -> EscapeAction { } } +fn is_composer_newline_key(key: KeyEvent) -> bool { + match key.code { + KeyCode::Char('j') => key.modifiers.contains(KeyModifiers::CONTROL), + KeyCode::Enter => { + key.modifiers.contains(KeyModifiers::ALT) + || (key.modifiers.contains(KeyModifiers::SHIFT) + && !key.modifiers.contains(KeyModifiers::CONTROL)) + } + _ => false, + } +} + fn handle_history_search_key(app: &mut App, key: KeyEvent) { match key.code { KeyCode::Enter => { diff --git a/crates/tui/src/tui/ui/tests.rs b/crates/tui/src/tui/ui/tests.rs index e7478115..e78b3f6e 100644 --- a/crates/tui/src/tui/ui/tests.rs +++ b/crates/tui/src/tui/ui/tests.rs @@ -16,6 +16,34 @@ use std::process::Command; use std::time::{Duration, Instant}; use tempfile::TempDir; +#[test] +fn composer_newline_shortcuts_do_not_steal_ctrl_enter() { + assert!(is_composer_newline_key(KeyEvent::new( + KeyCode::Char('j'), + KeyModifiers::CONTROL, + ))); + assert!(is_composer_newline_key(KeyEvent::new( + KeyCode::Enter, + KeyModifiers::ALT, + ))); + assert!(is_composer_newline_key(KeyEvent::new( + KeyCode::Enter, + KeyModifiers::SHIFT, + ))); + assert!(!is_composer_newline_key(KeyEvent::new( + KeyCode::Enter, + KeyModifiers::NONE, + ))); + assert!(!is_composer_newline_key(KeyEvent::new( + KeyCode::Enter, + KeyModifiers::CONTROL, + ))); + assert!(!is_composer_newline_key(KeyEvent::new( + KeyCode::Enter, + KeyModifiers::CONTROL | KeyModifiers::SHIFT, + ))); +} + #[test] fn selection_point_from_position_ignores_top_padding() { let area = Rect {