use std::sync::mpsc as std_mpsc;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
use tokio::sync::mpsc::UnboundedSender;
use crate::repl::tui::app::App;
use crate::repl::tui::event::UiEvent;
pub(super) fn install_confirm_handler(ui_tx: UnboundedSender<UiEvent>) {
let handler = Box::new(
move |prompt: &str,
choices: &[String],
default_index: usize,
kind: crate::tools::utils::ConfirmationType| {
let (reply_tx, reply_rx) = std_mpsc::channel::<usize>();
let event = UiEvent::ConfirmRequest {
prompt: prompt.to_string(),
choices: choices.to_vec(),
default_index,
kind,
responder: reply_tx,
};
if ui_tx.send(event).is_err() {
return default_index;
}
reply_rx.recv().unwrap_or(default_index)
},
);
crate::tools::utils::set_confirm_handler(handler);
}
pub(super) fn handle_confirmation_key(app: &mut App, key: KeyEvent) {
if key.kind != KeyEventKind::Press && key.kind != KeyEventKind::Repeat {
return;
}
let ctrl = key.modifiers.contains(KeyModifiers::CONTROL);
let Some(confirmation) = app.confirmation.as_mut() else {
return;
};
match key.code {
KeyCode::Up => {
if confirmation.cursor > 0 {
confirmation.cursor -= 1;
}
}
KeyCode::Down => {
if confirmation.cursor + 1 < confirmation.choices.len() {
confirmation.cursor += 1;
}
}
KeyCode::Enter => {
if let Some(c) = app.confirmation.take() {
let _ = c.responder.send(c.cursor);
}
}
KeyCode::Esc => {
if let Some(c) = app.confirmation.take() {
let _ = c.responder.send(c.default_index);
}
}
KeyCode::Char('c') if ctrl => {
if let Some(c) = app.confirmation.take() {
let _ = c.responder.send(c.default_index);
}
}
KeyCode::Char(ch) if !ctrl => {
if let Some(idx) = digit_shortcut(ch, confirmation.choices.len()) {
if let Some(c) = app.confirmation.take() {
let _ = c.responder.send(idx);
}
return;
}
if let Some(idx) = letter_shortcut(ch, &confirmation.choices, confirmation.cursor) {
confirmation.cursor = idx;
return;
}
match ch {
'k' if confirmation.cursor > 0 => {
confirmation.cursor -= 1;
}
'j' if confirmation.cursor + 1 < confirmation.choices.len() => {
confirmation.cursor += 1;
}
_ => {}
}
}
_ => {}
}
}
fn digit_shortcut(ch: char, choices_len: usize) -> Option<usize> {
let n = ch.to_digit(10)?;
if n == 0 {
return None;
}
let idx = (n as usize).checked_sub(1)?;
if idx < choices_len { Some(idx) } else { None }
}
fn letter_shortcut(ch: char, choices: &[String], cursor: usize) -> Option<usize> {
let needle = ch.to_ascii_lowercase();
if !needle.is_ascii_alphabetic() {
return None;
}
let n = choices.len();
(1..=n).find_map(|offset| {
let idx = (cursor + offset) % n;
let first = choices[idx].chars().next()?.to_ascii_lowercase();
(first == needle).then_some(idx)
})
}