use promkit_widgets::{listbox::Listbox, text::Text, text_editor};
use crate::{
core::crossterm::{
event::{Event, KeyCode, KeyEvent, KeyEventKind, KeyEventState, KeyModifiers},
style::ContentStyle,
},
preset::readline::{Focus, Readline},
Signal,
};
pub async fn default(event: &Event, ctx: &mut Readline) -> anyhow::Result<Signal> {
match event {
Event::Resize(width, height) => {
ctx.render(*width, *height).await?;
}
Event::Key(KeyEvent {
code: KeyCode::Char('c'),
modifiers: KeyModifiers::CONTROL,
kind: KeyEventKind::Press,
state: KeyEventState::NONE,
}) => return Err(anyhow::anyhow!("ctrl+c")),
_ => {
match ctx.focus {
Focus::Readline => {
return readline(event, ctx).await;
}
Focus::Suggestion => {
return suggestion(event, ctx).await;
}
}
}
}
Ok(Signal::Continue)
}
pub async fn readline(event: &Event, ctx: &mut Readline) -> anyhow::Result<Signal> {
match event {
Event::Key(KeyEvent {
code: KeyCode::Enter,
modifiers: KeyModifiers::NONE,
kind: KeyEventKind::Press,
state: KeyEventState::NONE,
}) => {
let text = ctx.readline.texteditor.text_without_cursor().to_string();
let valid = ctx
.validator
.as_ref()
.map(|validator| {
let valid = validator.validate(&text);
if !valid {
ctx.error_message.text =
Text::from(validator.generate_error_message(&text));
}
valid
})
.unwrap_or(true);
return {
if valid {
if let Some(ref mut history) = &mut ctx.readline.history {
history.insert(text);
}
ctx.readline.config.active_char_style = ContentStyle::default();
Ok(Signal::Quit)
} else {
Ok(Signal::Continue)
}
};
}
Event::Key(KeyEvent {
code: KeyCode::Tab,
modifiers: KeyModifiers::NONE,
kind: KeyEventKind::Press,
state: KeyEventState::NONE,
}) => {
if let Some(suggest) = &ctx.suggest {
let text = ctx.readline.texteditor.text_without_cursor().to_string();
if let Some(candidates) = suggest.prefix_search(text) {
ctx.suggestions.listbox = Listbox::from(candidates);
ctx.readline
.texteditor
.replace(&ctx.suggestions.listbox.get().to_string());
ctx.focus = Focus::Suggestion;
}
}
}
Event::Key(KeyEvent {
code: KeyCode::Left,
modifiers: KeyModifiers::NONE,
kind: KeyEventKind::Press,
state: KeyEventState::NONE,
}) => {
ctx.readline.texteditor.backward();
}
Event::Key(KeyEvent {
code: KeyCode::Right,
modifiers: KeyModifiers::NONE,
kind: KeyEventKind::Press,
state: KeyEventState::NONE,
}) => {
ctx.readline.texteditor.forward();
}
Event::Key(KeyEvent {
code: KeyCode::Char('a'),
modifiers: KeyModifiers::CONTROL,
kind: KeyEventKind::Press,
state: KeyEventState::NONE,
}) => ctx.readline.texteditor.move_to_head(),
Event::Key(KeyEvent {
code: KeyCode::Char('e'),
modifiers: KeyModifiers::CONTROL,
kind: KeyEventKind::Press,
state: KeyEventState::NONE,
}) => ctx.readline.texteditor.move_to_tail(),
Event::Key(KeyEvent {
code: KeyCode::Char('b'),
modifiers: KeyModifiers::ALT,
kind: KeyEventKind::Press,
state: KeyEventState::NONE,
}) => ctx
.readline
.texteditor
.move_to_previous_nearest(&ctx.readline.config.word_break_chars),
Event::Key(KeyEvent {
code: KeyCode::Char('f'),
modifiers: KeyModifiers::ALT,
kind: KeyEventKind::Press,
state: KeyEventState::NONE,
}) => ctx
.readline
.texteditor
.move_to_next_nearest(&ctx.readline.config.word_break_chars),
Event::Key(KeyEvent {
code: KeyCode::Backspace,
modifiers: KeyModifiers::NONE,
kind: KeyEventKind::Press,
state: KeyEventState::NONE,
}) => ctx.readline.texteditor.erase(),
Event::Key(KeyEvent {
code: KeyCode::Char('u'),
modifiers: KeyModifiers::CONTROL,
kind: KeyEventKind::Press,
state: KeyEventState::NONE,
}) => ctx.readline.texteditor.erase_all(),
Event::Key(KeyEvent {
code: KeyCode::Char('w'),
modifiers: KeyModifiers::CONTROL,
kind: KeyEventKind::Press,
state: KeyEventState::NONE,
}) => ctx
.readline
.texteditor
.erase_to_previous_nearest(&ctx.readline.config.word_break_chars),
Event::Key(KeyEvent {
code: KeyCode::Char('d'),
modifiers: KeyModifiers::ALT,
kind: KeyEventKind::Press,
state: KeyEventState::NONE,
}) => ctx
.readline
.texteditor
.erase_to_next_nearest(&ctx.readline.config.word_break_chars),
Event::Key(KeyEvent {
code: KeyCode::Up,
modifiers: KeyModifiers::NONE,
kind: KeyEventKind::Press,
state: KeyEventState::NONE,
}) => {
if let Some(ref mut history) = &mut ctx.readline.history {
if history.backward() {
ctx.readline.texteditor.replace(&history.get())
}
}
}
Event::Key(KeyEvent {
code: KeyCode::Down,
modifiers: KeyModifiers::NONE,
kind: KeyEventKind::Press,
state: KeyEventState::NONE,
}) => {
if let Some(ref mut history) = &mut ctx.readline.history {
if history.forward() {
ctx.readline.texteditor.replace(&history.get())
}
}
}
Event::Key(KeyEvent {
code: KeyCode::Char(ch),
modifiers: KeyModifiers::NONE,
kind: KeyEventKind::Press,
state: KeyEventState::NONE,
})
| Event::Key(KeyEvent {
code: KeyCode::Char(ch),
modifiers: KeyModifiers::SHIFT,
kind: KeyEventKind::Press,
state: KeyEventState::NONE,
}) => match ctx.readline.config.edit_mode {
text_editor::Mode::Insert => ctx.readline.texteditor.insert(*ch),
text_editor::Mode::Overwrite => ctx.readline.texteditor.overwrite(*ch),
},
_ => (),
}
Ok(Signal::Continue)
}
pub async fn suggestion(event: &Event, ctx: &mut Readline) -> anyhow::Result<Signal> {
match event {
Event::Key(KeyEvent {
code: KeyCode::Tab,
modifiers: KeyModifiers::NONE,
kind: KeyEventKind::Press,
state: KeyEventState::NONE,
})
| Event::Key(KeyEvent {
code: KeyCode::Down,
modifiers: KeyModifiers::NONE,
kind: KeyEventKind::Press,
state: KeyEventState::NONE,
}) => {
ctx.suggestions.listbox.forward();
ctx.readline
.texteditor
.replace(&ctx.suggestions.listbox.get().to_string());
}
Event::Key(KeyEvent {
code: KeyCode::Up,
modifiers: KeyModifiers::NONE,
kind: KeyEventKind::Press,
state: KeyEventState::NONE,
}) => {
ctx.suggestions.listbox.backward();
ctx.readline
.texteditor
.replace(&ctx.suggestions.listbox.get().to_string());
}
_ => {
ctx.suggestions.listbox = Listbox::from(Vec::<String>::new());
ctx.focus = Focus::Readline;
}
}
Ok(Signal::Continue)
}