Skip to content

Commit 9358dba

Browse files
evansenterclaude
andcommitted
feat: add multiline input support with Shift+Enter and bracketed paste
- Enable kitty keyboard protocol for Shift+Enter support in iTerm2, kitty, WezTerm, and alacritty - Add Alt+Enter as fallback for terminals without kitty protocol - Enable bracketed paste mode so pasted multiline content doesn't auto-submit - Add documentation tests explaining manual testing requirements (PTY environments don't support these terminal features) Addresses #24 - multiline input support in REPL Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 8cb4664 commit 9358dba

2 files changed

Lines changed: 88 additions & 3 deletions

File tree

src/main.rs

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ use anyhow::Result;
22
use clap::Parser;
33
use colored::Colorize;
44
use genai_rs::Client;
5-
use reedline::{FileBackedHistory, Prompt, PromptHistorySearch, Reedline, Signal};
5+
use reedline::{
6+
default_emacs_keybindings, EditCommand, Emacs, FileBackedHistory, KeyCode, KeyModifiers,
7+
Prompt, PromptHistorySearch, Reedline, ReedlineEvent, Signal, kitty_protocol_available,
8+
};
69
use serde::Deserialize;
710
use std::borrow::Cow;
811
use std::env;
@@ -663,8 +666,35 @@ fn spawn_reedline_thread(
663666
}
664667
};
665668

666-
// Create reedline editor
667-
let mut line_editor = Reedline::create();
669+
// Create reedline editor with custom keybindings
670+
let mut keybindings = default_emacs_keybindings();
671+
// Shift+Enter inserts a newline for multiline input
672+
// Requires kitty keyboard protocol for Shift modifier detection
673+
keybindings.add_binding(
674+
KeyModifiers::SHIFT,
675+
KeyCode::Enter,
676+
ReedlineEvent::Edit(vec![EditCommand::InsertNewline]),
677+
);
678+
// Also bind Alt+Enter as fallback for terminals without kitty protocol
679+
keybindings.add_binding(
680+
KeyModifiers::ALT,
681+
KeyCode::Enter,
682+
ReedlineEvent::Edit(vec![EditCommand::InsertNewline]),
683+
);
684+
let edit_mode = Box::new(Emacs::new(keybindings));
685+
686+
// Enable kitty keyboard protocol for better modifier key detection
687+
// (Shift+Enter, etc.) in supported terminals (iTerm2, kitty, WezTerm, alacritty)
688+
let use_kitty = kitty_protocol_available();
689+
if use_kitty {
690+
tracing::debug!("Kitty keyboard protocol available, enabling enhanced key detection");
691+
}
692+
693+
let mut line_editor = Reedline::create()
694+
.with_edit_mode(edit_mode)
695+
.use_kitty_keyboard_enhancement(use_kitty)
696+
// Enable bracketed paste so multiline pastes don't auto-submit on each newline
697+
.use_bracketed_paste(true);
668698
if let Some(h) = history {
669699
line_editor = line_editor.with_history(h);
670700
}

tests/terminal_tests.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -764,3 +764,58 @@ fn test_ctrl_d_exits() {
764764
// Process should exit
765765
wait_for_exit(&mut session).expect("Process should have exited");
766766
}
767+
768+
// ============================================================================
769+
// Multiline Input Tests
770+
// ============================================================================
771+
772+
/// Multiline input via Shift+Enter or Alt+Enter.
773+
///
774+
/// **Manual testing required** - PTY environments don't support the kitty keyboard
775+
/// protocol needed for Shift+Enter, and Alt+Enter escape sequences are interpreted
776+
/// differently across terminal emulators.
777+
///
778+
/// To test manually:
779+
/// 1. Run `cargo run` or the release binary
780+
/// 2. Type some text
781+
/// 3. Press Shift+Enter (in iTerm2, kitty, WezTerm, alacritty) or Alt+Enter
782+
/// 4. Should insert a newline and show the multiline indicator (" ")
783+
/// 5. Type more text and press Enter to submit the full multiline input
784+
#[test]
785+
#[ignore = "Manual testing only - PTY doesn't support kitty keyboard protocol"]
786+
fn test_multiline_input_manual() {
787+
// This test exists as documentation for manual testing.
788+
// Automated PTY testing of keyboard shortcuts with modifiers is unreliable
789+
// because PTYs don't support the kitty keyboard protocol.
790+
eprintln!("This test requires manual verification:");
791+
eprintln!("1. Run clemini");
792+
eprintln!("2. Type text, press Shift+Enter or Alt+Enter");
793+
eprintln!("3. Verify newline is inserted with multiline indicator");
794+
}
795+
796+
/// Bracketed paste for multiline content.
797+
///
798+
/// **Manual testing required** - PTY environments don't support bracketed paste mode.
799+
/// Bracketed paste is a terminal emulator feature where the terminal wraps pasted
800+
/// content with `ESC[200~` ... `ESC[201~` escape sequences. PTY testing libraries
801+
/// don't emulate this terminal feature.
802+
///
803+
/// To test manually:
804+
/// 1. Run `cargo run` or the release binary
805+
/// 2. Copy multiline text to clipboard
806+
/// 3. Paste into clemini (Cmd+V / Ctrl+Shift+V)
807+
/// 4. Content should appear in buffer with newlines preserved
808+
/// 5. Press Enter to submit the entire multiline input
809+
///
810+
/// Without bracketed paste, each newline would submit immediately.
811+
#[test]
812+
#[ignore = "Manual testing only - PTY doesn't support bracketed paste mode"]
813+
fn test_bracketed_paste_manual() {
814+
// This test exists as documentation for manual testing.
815+
// PTYs don't support bracketed paste mode - it's a terminal emulator feature.
816+
eprintln!("This test requires manual verification:");
817+
eprintln!("1. Run clemini");
818+
eprintln!("2. Copy multiline text and paste it");
819+
eprintln!("3. Verify all lines appear in the input buffer");
820+
eprintln!("4. Press Enter to submit");
821+
}

0 commit comments

Comments
 (0)