1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
//! Search modal key handler
use crossterm::event::{KeyCode, KeyEvent};
use crate::studio::events::SideEffect;
use crate::studio::state::{Modal, Notification, StudioState};
/// Handle key events in search modal
pub fn handle(state: &mut StudioState, key: KeyEvent) -> Vec<SideEffect> {
// Get current query for filtering
let query = if let Some(Modal::Search { query, .. }) = &state.modal {
query.clone()
} else {
return vec![];
};
match key.code {
KeyCode::Esc => {
state.close_modal();
vec![]
}
KeyCode::Enter => {
// Select the current file and jump to it
if let Some(Modal::Search {
results, selected, ..
}) = &state.modal
{
// Filter results by query
let filtered: Vec<_> = results
.iter()
.filter(|r| {
query.is_empty() || r.to_lowercase().contains(&query.to_lowercase())
})
.collect();
if let Some(file_path) = filtered.get(*selected) {
let path_str = (*file_path).clone();
let path = std::path::Path::new(&path_str);
state.close_modal();
// Try to select file in current mode's diff view
match state.active_mode {
crate::studio::state::Mode::Commit => {
state.modes.commit.diff_view.select_file_by_path(path);
}
crate::studio::state::Mode::Review => {
state.modes.review.diff_view.select_file_by_path(path);
}
crate::studio::state::Mode::PR => {
state.modes.pr.diff_view.select_file_by_path(path);
}
_ => {}
}
state.notify(Notification::info(format!("Jumped to {}", path_str)));
return vec![];
}
}
state.close_modal();
vec![]
}
KeyCode::Up => {
if let Some(Modal::Search { selected, .. }) = &mut state.modal {
*selected = selected.saturating_sub(1);
}
state.mark_dirty();
vec![]
}
KeyCode::Down => {
if let Some(Modal::Search {
results,
selected,
query,
}) = &mut state.modal
{
let filtered_len = results
.iter()
.filter(|r| {
query.is_empty() || r.to_lowercase().contains(&query.to_lowercase())
})
.count();
if *selected + 1 < filtered_len {
*selected += 1;
}
}
state.mark_dirty();
vec![]
}
KeyCode::Char(c) => {
if let Some(Modal::Search {
query, selected, ..
}) = &mut state.modal
{
query.push(c);
*selected = 0; // Reset selection on filter change
}
state.mark_dirty();
vec![]
}
KeyCode::Backspace => {
if let Some(Modal::Search {
query, selected, ..
}) = &mut state.modal
{
query.pop();
*selected = 0; // Reset selection on filter change
}
state.mark_dirty();
vec![]
}
_ => vec![],
}
}