use ratatui::crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use crate::app::App;
use crate::clipboard;
pub fn handle_results_pane_key(app: &mut App, key: KeyEvent) {
match key.code {
KeyCode::Char('/') => {
crate::search::search_events::open_search(app);
}
KeyCode::Char('?') => {
app.help.visible = !app.help.visible;
}
KeyCode::Char('y') => {
clipboard::clipboard_events::handle_yank_key(app, app.clipboard_backend);
}
KeyCode::Up | KeyCode::Char('k') => {
app.results_scroll.scroll_up(1);
}
KeyCode::Down | KeyCode::Char('j') => {
app.results_scroll.scroll_down(1);
}
KeyCode::Char('K') => {
app.results_scroll.scroll_up(10);
}
KeyCode::Char('J') => {
app.results_scroll.scroll_down(10);
}
KeyCode::Left | KeyCode::Char('h') => {
app.results_scroll.scroll_left(1);
}
KeyCode::Right | KeyCode::Char('l') => {
app.results_scroll.scroll_right(1);
}
KeyCode::Char('H') => {
app.results_scroll.scroll_left(10);
}
KeyCode::Char('L') => {
app.results_scroll.scroll_right(10);
}
KeyCode::Char('0') | KeyCode::Char('^') => {
app.results_scroll.jump_to_left();
}
KeyCode::Char('$') => {
app.results_scroll.jump_to_right();
}
KeyCode::Home | KeyCode::Char('g') => {
app.results_scroll.jump_to_top();
}
KeyCode::End | KeyCode::Char('G') => {
app.results_scroll.jump_to_bottom();
}
KeyCode::PageUp | KeyCode::Char('u')
if key.code == KeyCode::PageUp || key.modifiers.contains(KeyModifiers::CONTROL) =>
{
app.results_scroll.page_up();
}
KeyCode::PageDown | KeyCode::Char('d')
if key.code == KeyCode::PageDown || key.modifiers.contains(KeyModifiers::CONTROL) =>
{
app.results_scroll.page_down();
}
_ => {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::app::Focus;
use crate::test_utils::test_helpers::{app_with_query, key, key_with_mods, test_app};
#[test]
fn test_j_scrolls_down_one_line() {
let mut app = app_with_query(".");
app.focus = Focus::ResultsPane;
let content: String = (0..20).map(|i| format!("line{}\n", i)).collect();
app.query.result = Ok(content);
let line_count = app.results_line_count_u32();
app.results_scroll.update_bounds(line_count, 10);
app.results_scroll.offset = 0;
app.handle_key_event(key(KeyCode::Char('j')));
assert_eq!(app.results_scroll.offset, 1);
}
#[test]
fn test_k_scrolls_up_one_line() {
let mut app = app_with_query(".");
app.focus = Focus::ResultsPane;
app.results_scroll.offset = 5;
app.handle_key_event(key(KeyCode::Char('k')));
assert_eq!(app.results_scroll.offset, 4);
}
#[test]
fn test_k_at_top_stays_at_zero() {
let mut app = app_with_query(".");
app.focus = Focus::ResultsPane;
app.results_scroll.offset = 0;
app.handle_key_event(key(KeyCode::Char('k')));
assert_eq!(app.results_scroll.offset, 0);
}
#[test]
fn test_capital_j_scrolls_down_ten_lines() {
let mut app = app_with_query(".");
app.focus = Focus::ResultsPane;
let content: String = (0..30).map(|i| format!("line{}\n", i)).collect();
app.query.result = Ok(content);
let line_count = app.results_line_count_u32();
app.results_scroll.update_bounds(line_count, 10);
app.results_scroll.offset = 5;
app.handle_key_event(key(KeyCode::Char('J')));
assert_eq!(app.results_scroll.offset, 15);
}
#[test]
fn test_capital_k_scrolls_up_ten_lines() {
let mut app = app_with_query(".");
app.focus = Focus::ResultsPane;
app.results_scroll.offset = 20;
app.handle_key_event(key(KeyCode::Char('K')));
assert_eq!(app.results_scroll.offset, 10);
}
#[test]
fn test_g_jumps_to_top() {
let mut app = app_with_query(".");
app.focus = Focus::ResultsPane;
app.results_scroll.offset = 50;
app.handle_key_event(key(KeyCode::Char('g')));
assert_eq!(app.results_scroll.offset, 0);
}
#[test]
fn test_capital_g_jumps_to_bottom() {
let json = r#"{"line1": 1, "line2": 2, "line3": 3}"#;
let mut app = test_app(json);
app.input.textarea.insert_str(".");
app.focus = Focus::ResultsPane;
app.results_scroll.offset = 0;
app.results_scroll.viewport_height = 2;
let line_count = app.results_line_count_u32();
app.results_scroll.update_bounds(line_count, 2);
let max_scroll = app.results_scroll.max_offset;
app.handle_key_event(key(KeyCode::Char('G')));
assert_eq!(app.results_scroll.offset, max_scroll);
}
#[test]
fn test_page_up_scrolls_half_page() {
let mut app = app_with_query(".");
app.focus = Focus::ResultsPane;
app.results_scroll.offset = 20;
app.results_scroll.viewport_height = 20;
app.handle_key_event(key(KeyCode::PageUp));
assert_eq!(app.results_scroll.offset, 10);
}
#[test]
fn test_page_down_scrolls_half_page() {
let mut app = app_with_query(".");
app.focus = Focus::ResultsPane;
let content: String = (0..50).map(|i| format!("line{}\n", i)).collect();
app.query.result = Ok(content);
let line_count = app.results_line_count_u32();
app.results_scroll.update_bounds(line_count, 20);
app.results_scroll.offset = 0;
app.handle_key_event(key(KeyCode::PageDown));
assert_eq!(app.results_scroll.offset, 10);
}
#[test]
fn test_ctrl_u_scrolls_half_page_up() {
let mut app = app_with_query(".");
app.focus = Focus::ResultsPane;
app.results_scroll.offset = 20;
app.results_scroll.viewport_height = 20;
app.handle_key_event(key_with_mods(KeyCode::Char('u'), KeyModifiers::CONTROL));
assert_eq!(app.results_scroll.offset, 10);
}
#[test]
fn test_ctrl_d_scrolls_half_page_down() {
let mut app = app_with_query(".");
app.focus = Focus::ResultsPane;
let content: String = (0..50).map(|i| format!("line{}\n", i)).collect();
app.query.result = Ok(content);
let line_count = app.results_line_count_u32();
app.results_scroll.update_bounds(line_count, 20);
app.results_scroll.offset = 0;
app.handle_key_event(key_with_mods(KeyCode::Char('d'), KeyModifiers::CONTROL));
assert_eq!(app.results_scroll.offset, 10);
}
#[test]
fn test_up_arrow_scrolls_in_results_pane() {
let mut app = app_with_query(".");
app.focus = Focus::ResultsPane;
app.results_scroll.offset = 5;
app.handle_key_event(key(KeyCode::Up));
assert_eq!(app.results_scroll.offset, 4);
}
#[test]
fn test_down_arrow_scrolls_in_results_pane() {
let mut app = app_with_query(".");
app.focus = Focus::ResultsPane;
let content: String = (0..20).map(|i| format!("line{}\n", i)).collect();
app.query.result = Ok(content);
let line_count = app.results_line_count_u32();
app.results_scroll.update_bounds(line_count, 10);
app.results_scroll.offset = 0;
app.handle_key_event(key(KeyCode::Down));
assert_eq!(app.results_scroll.offset, 1);
}
#[test]
fn test_home_jumps_to_top() {
let mut app = app_with_query(".");
app.focus = Focus::ResultsPane;
app.results_scroll.offset = 50;
app.handle_key_event(key(KeyCode::Home));
assert_eq!(app.results_scroll.offset, 0);
}
#[test]
fn test_scroll_clamped_to_max() {
let mut app = app_with_query("");
app.focus = Focus::ResultsPane;
app.query.result = Ok("line1\nline2\nline3".to_string());
let line_count = app.results_line_count_u32();
app.results_scroll.update_bounds(line_count, 10);
assert_eq!(app.results_scroll.max_offset, 0);
app.handle_key_event(key(KeyCode::Char('j')));
assert_eq!(app.results_scroll.offset, 0);
for _ in 0..100 {
app.handle_key_event(key(KeyCode::Char('j')));
}
assert_eq!(app.results_scroll.offset, 0);
}
#[test]
fn test_scroll_clamped_with_content() {
let mut app = app_with_query("");
app.focus = Focus::ResultsPane;
let content: String = (0..20).map(|i| format!("line{}\n", i)).collect();
app.query.result = Ok(content);
let line_count = app.results_line_count_u32();
app.results_scroll.update_bounds(line_count, 10);
assert_eq!(app.results_scroll.max_offset, 10);
for _ in 0..100 {
app.handle_key_event(key(KeyCode::Char('j')));
}
assert_eq!(app.results_scroll.offset, 10);
}
#[test]
fn test_scroll_page_down_clamped() {
let mut app = app_with_query("");
app.focus = Focus::ResultsPane;
let content: String = (0..15).map(|i| format!("line{}\n", i)).collect();
app.query.result = Ok(content);
let line_count = app.results_line_count_u32();
app.results_scroll.update_bounds(line_count, 10);
assert_eq!(app.results_scroll.max_offset, 5);
app.handle_key_event(key(KeyCode::PageDown));
assert_eq!(app.results_scroll.offset, 5);
app.handle_key_event(key(KeyCode::PageDown));
assert_eq!(app.results_scroll.offset, 5);
}
#[test]
fn test_scroll_j_clamped() {
let mut app = app_with_query("");
app.focus = Focus::ResultsPane;
let content: String = (0..5).map(|i| format!("line{}\n", i)).collect();
app.query.result = Ok(content);
let line_count = app.results_line_count_u32();
app.results_scroll.update_bounds(line_count, 3);
assert_eq!(app.results_scroll.max_offset, 2);
app.handle_key_event(key(KeyCode::Char('J')));
assert_eq!(app.results_scroll.offset, 2);
}
#[test]
fn test_question_mark_toggles_help_in_results_pane() {
let mut app = app_with_query(".");
app.focus = Focus::ResultsPane;
app.handle_key_event(key(KeyCode::Char('?')));
assert!(app.help.visible);
}
fn app_with_wide_content() -> App {
let mut app = app_with_query(".");
app.focus = Focus::ResultsPane;
let content: String = (0..10)
.map(|i| format!("{}{}\n", i, "x".repeat(100)))
.collect();
app.query.result = Ok(content);
app.results_scroll.update_h_bounds(101, 40); app
}
#[test]
fn test_h_scrolls_left_one_column() {
let mut app = app_with_wide_content();
app.results_scroll.h_offset = 10;
app.handle_key_event(key(KeyCode::Char('h')));
assert_eq!(app.results_scroll.h_offset, 9);
}
#[test]
fn test_l_scrolls_right_one_column() {
let mut app = app_with_wide_content();
app.results_scroll.h_offset = 0;
app.handle_key_event(key(KeyCode::Char('l')));
assert_eq!(app.results_scroll.h_offset, 1);
}
#[test]
fn test_left_arrow_scrolls_left() {
let mut app = app_with_wide_content();
app.results_scroll.h_offset = 10;
app.handle_key_event(key(KeyCode::Left));
assert_eq!(app.results_scroll.h_offset, 9);
}
#[test]
fn test_right_arrow_scrolls_right() {
let mut app = app_with_wide_content();
app.results_scroll.h_offset = 0;
app.handle_key_event(key(KeyCode::Right));
assert_eq!(app.results_scroll.h_offset, 1);
}
#[test]
fn test_capital_h_scrolls_left_ten_columns() {
let mut app = app_with_wide_content();
app.results_scroll.h_offset = 30;
app.handle_key_event(key(KeyCode::Char('H')));
assert_eq!(app.results_scroll.h_offset, 20);
}
#[test]
fn test_capital_l_scrolls_right_ten_columns() {
let mut app = app_with_wide_content();
app.results_scroll.h_offset = 0;
app.handle_key_event(key(KeyCode::Char('L')));
assert_eq!(app.results_scroll.h_offset, 10);
}
#[test]
fn test_zero_jumps_to_left_edge() {
let mut app = app_with_wide_content();
app.results_scroll.h_offset = 50;
app.handle_key_event(key(KeyCode::Char('0')));
assert_eq!(app.results_scroll.h_offset, 0);
}
#[test]
fn test_caret_jumps_to_left_edge() {
let mut app = app_with_wide_content();
app.results_scroll.h_offset = 50;
app.handle_key_event(key(KeyCode::Char('^')));
assert_eq!(app.results_scroll.h_offset, 0);
}
#[test]
fn test_dollar_jumps_to_right_edge() {
let mut app = app_with_wide_content();
app.results_scroll.h_offset = 0;
app.handle_key_event(key(KeyCode::Char('$')));
assert_eq!(app.results_scroll.h_offset, 61); }
#[test]
fn test_h_scroll_left_clamped_at_zero() {
let mut app = app_with_wide_content();
app.results_scroll.h_offset = 0;
app.handle_key_event(key(KeyCode::Char('h')));
assert_eq!(app.results_scroll.h_offset, 0);
}
#[test]
fn test_l_scroll_right_clamped_at_max() {
let mut app = app_with_wide_content();
app.results_scroll.h_offset = 61;
app.handle_key_event(key(KeyCode::Char('l')));
assert_eq!(app.results_scroll.h_offset, 61);
}
#[test]
fn test_end_jumps_to_bottom() {
let mut app = app_with_query(".");
app.focus = Focus::ResultsPane;
let content: String = (0..20).map(|i| format!("line{}\n", i)).collect();
app.query.result = Ok(content);
app.results_scroll.update_bounds(20, 10);
app.results_scroll.offset = 0;
app.handle_key_event(key(KeyCode::End));
assert_eq!(app.results_scroll.offset, 10); }
}