[feat] make approval key matching case insensitive (#1862)
This commit is contained in:
@@ -47,6 +47,8 @@ pub(crate) enum ApprovalRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Options displayed in the *select* mode.
|
/// Options displayed in the *select* mode.
|
||||||
|
///
|
||||||
|
/// The `key` is matched case-insensitively.
|
||||||
struct SelectOption {
|
struct SelectOption {
|
||||||
label: Line<'static>,
|
label: Line<'static>,
|
||||||
description: &'static str,
|
description: &'static str,
|
||||||
@@ -187,6 +189,16 @@ impl UserApprovalWidget<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Normalize a key for comparison.
|
||||||
|
/// - For `KeyCode::Char`, converts to lowercase for case-insensitive matching.
|
||||||
|
/// - Other key codes are returned unchanged.
|
||||||
|
fn normalize_keycode(code: KeyCode) -> KeyCode {
|
||||||
|
match code {
|
||||||
|
KeyCode::Char(c) => KeyCode::Char(c.to_ascii_lowercase()),
|
||||||
|
other => other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Handle Ctrl-C pressed by the user while the modal is visible.
|
/// Handle Ctrl-C pressed by the user while the modal is visible.
|
||||||
/// Behaves like pressing Escape: abort the request and close the modal.
|
/// Behaves like pressing Escape: abort the request and close the modal.
|
||||||
pub(crate) fn on_ctrl_c(&mut self) {
|
pub(crate) fn on_ctrl_c(&mut self) {
|
||||||
@@ -210,7 +222,12 @@ impl UserApprovalWidget<'_> {
|
|||||||
self.send_decision(ReviewDecision::Abort);
|
self.send_decision(ReviewDecision::Abort);
|
||||||
}
|
}
|
||||||
other => {
|
other => {
|
||||||
if let Some(opt) = self.select_options.iter().find(|opt| opt.key == other) {
|
let normalized = Self::normalize_keycode(other);
|
||||||
|
if let Some(opt) = self
|
||||||
|
.select_options
|
||||||
|
.iter()
|
||||||
|
.find(|opt| Self::normalize_keycode(opt.key) == normalized)
|
||||||
|
{
|
||||||
self.send_decision(opt.decision);
|
self.send_decision(opt.decision);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -330,3 +347,59 @@ impl WidgetRef for &UserApprovalWidget<'_> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crossterm::event::KeyCode;
|
||||||
|
use crossterm::event::KeyEvent;
|
||||||
|
use crossterm::event::KeyModifiers;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::sync::mpsc::channel;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lowercase_shortcut_is_accepted() {
|
||||||
|
let (tx_raw, rx) = channel::<AppEvent>();
|
||||||
|
let tx = AppEventSender::new(tx_raw);
|
||||||
|
let req = ApprovalRequest::Exec {
|
||||||
|
id: "1".to_string(),
|
||||||
|
command: vec!["echo".to_string()],
|
||||||
|
cwd: PathBuf::new(),
|
||||||
|
reason: None,
|
||||||
|
};
|
||||||
|
let mut widget = UserApprovalWidget::new(req, tx);
|
||||||
|
widget.handle_key_event(KeyEvent::new(KeyCode::Char('y'), KeyModifiers::NONE));
|
||||||
|
assert!(widget.is_complete());
|
||||||
|
let events: Vec<AppEvent> = rx.try_iter().collect();
|
||||||
|
assert!(events.iter().any(|e| matches!(
|
||||||
|
e,
|
||||||
|
AppEvent::CodexOp(Op::ExecApproval {
|
||||||
|
decision: ReviewDecision::Approved,
|
||||||
|
..
|
||||||
|
})
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn uppercase_shortcut_is_accepted() {
|
||||||
|
let (tx_raw, rx) = channel::<AppEvent>();
|
||||||
|
let tx = AppEventSender::new(tx_raw);
|
||||||
|
let req = ApprovalRequest::Exec {
|
||||||
|
id: "2".to_string(),
|
||||||
|
command: vec!["echo".to_string()],
|
||||||
|
cwd: PathBuf::new(),
|
||||||
|
reason: None,
|
||||||
|
};
|
||||||
|
let mut widget = UserApprovalWidget::new(req, tx);
|
||||||
|
widget.handle_key_event(KeyEvent::new(KeyCode::Char('Y'), KeyModifiers::NONE));
|
||||||
|
assert!(widget.is_complete());
|
||||||
|
let events: Vec<AppEvent> = rx.try_iter().collect();
|
||||||
|
assert!(events.iter().any(|e| matches!(
|
||||||
|
e,
|
||||||
|
AppEvent::CodexOp(Op::ExecApproval {
|
||||||
|
decision: ReviewDecision::Approved,
|
||||||
|
..
|
||||||
|
})
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user