From a7f8776e03bdddbba1327fdffd8e62a3c134aa12 Mon Sep 17 00:00:00 2001 From: evalstate Date: Mon, 13 Jul 2026 18:39:36 +0100 Subject: [PATCH] enable shift key, word wrap, shift+enter newline in annotations --- README.md | 1 + src/app.rs | 14 +++++++++++++- src/render.rs | 32 ++++++++++++++++++++++++++++++++ src/runner.rs | 15 +++++++++++++++ src/terminal.rs | 25 ++++++++++++++++++++----- 5 files changed, 81 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 4a78faa..ef46002 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,7 @@ Keyboard controls: | `Shift-Up` / `Shift-Down` | Select a range; release Shift to write the comment | | `v` | Start or cancel range selection | | `Enter` | Comment on a source range, edit a focused comment, or save the editor | +| `Shift-Enter` / `Alt-Enter` | Insert a newline in a comment | | `Ctrl-O` | Insert a newline in a comment | | `Esc` | Cancel selection or editing | | `e` / `d` | Edit or delete a focused comment or one on the cursor line | diff --git a/src/app.rs b/src/app.rs index 60e132d..2033006 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,6 +1,6 @@ use std::collections::BTreeMap; -use ratatui_textarea::{CursorMove, TextArea}; +use ratatui_textarea::{CursorMove, TextArea, WrapMode}; use crate::{ domain::{Comment, ReviewDocument}, @@ -53,6 +53,7 @@ impl CommentEditor { }; textarea.set_placeholder_text("Write a comment…"); textarea.set_cursor_line_style(ratatui::style::Style::default()); + textarea.set_wrap_mode(WrapMode::WordOrGlyph); textarea.move_cursor(CursorMove::Bottom); textarea.move_cursor(CursorMove::End); Self { @@ -487,6 +488,17 @@ mod tests { assert_eq!(app.status.as_deref(), Some("Comment cannot be empty")); } + #[test] + fn comment_editor_wraps_words_and_long_tokens() { + let mut app = app(); + app.open_selected_editor(); + + assert_eq!( + app.editor.as_ref().unwrap().textarea.wrap_mode(), + WrapMode::WordOrGlyph + ); + } + #[test] fn whitespace_only_comment_stays_open() { let mut app = app(); diff --git a/src/render.rs b/src/render.rs index 91ee129..c297668 100644 --- a/src/render.rs +++ b/src/render.rs @@ -607,6 +607,38 @@ mod tests { assert!(terminal.backend().to_string().contains("▶─ code")); } + #[test] + fn annotation_editor_word_wraps_to_its_inner_width() { + let source = SourceBuffer::from_bytes("sample", b"one\n").unwrap(); + let review = ReviewDocument::empty(source.source_ref()); + let mut app = App::new(source, review); + app.open_selected_editor(); + app.editor + .as_mut() + .unwrap() + .textarea + .insert_str("alpha beta gamma delta epsilon"); + let backend = TestBackend::new(20, 9); + let mut terminal = Terminal::new(backend).unwrap(); + + terminal + .draw(|frame| drop(render_app(frame, &mut app))) + .unwrap(); + + let buffer = terminal.backend().buffer(); + let editor_rows = (2..7) + .map(|row| { + (1..19) + .map(|column| buffer[(column, row)].symbol()) + .collect::() + }) + .collect::>(); + assert!(editor_rows + .iter() + .any(|row| row.contains("alpha beta gamma"))); + assert!(editor_rows.iter().any(|row| row.contains("delta epsilon"))); + } + #[test] fn following_cursor_scrolls_long_documents() { let text = (1..=30) diff --git a/src/runner.rs b/src/runner.rs index dd3d3a7..17999f4 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -448,6 +448,21 @@ mod tests { assert_eq!(app.review.comments[0].body, "x"); } + #[test] + fn shift_enter_inserts_an_annotation_newline_without_submitting() { + let mut app = app(); + app.open_selected_editor(); + handle_key( + KeyEvent::new(KeyCode::Char('a'), KeyModifiers::NONE), + &mut app, + ); + + handle_key(KeyEvent::new(KeyCode::Enter, KeyModifiers::SHIFT), &mut app); + + assert_eq!(app.editor.as_ref().unwrap().body(), "a\n"); + assert!(app.review.comments.is_empty()); + } + #[test] fn shift_arrows_open_the_selected_range_when_shift_is_released() { let mut app = app(); diff --git a/src/terminal.rs b/src/terminal.rs index 7f20f13..c8743fe 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -47,11 +47,7 @@ impl TerminalGuard { PASTE_ENABLED.store(true, Ordering::SeqCst); execute!( tty, - PushKeyboardEnhancementFlags( - KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES - | KeyboardEnhancementFlags::REPORT_EVENT_TYPES - | KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES - ) + PushKeyboardEnhancementFlags(keyboard_enhancement_flags()) )?; KEYBOARD_ENHANCED.store(true, Ordering::SeqCst); if mouse_enabled { @@ -63,6 +59,13 @@ impl TerminalGuard { } } +fn keyboard_enhancement_flags() -> KeyboardEnhancementFlags { + KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES + | KeyboardEnhancementFlags::REPORT_EVENT_TYPES + | KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES + | KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS +} + impl Drop for TerminalGuard { fn drop(&mut self) { restore_terminal(); @@ -109,3 +112,15 @@ fn restore_terminal() { let _ = disable_raw_mode(); } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn enhanced_input_requests_shifted_characters() { + assert!( + keyboard_enhancement_flags().contains(KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS) + ); + } +}