mod app;
mod ui;
mod utils;
use crate::app::App;
use anyhow::Result;
use crossterm::{
event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEventKind, KeyModifiers},
execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use ratatui::{backend::CrosstermBackend, Terminal};
use std::{io, time::Duration};
#[tokio::main]
async fn main() -> Result<()> {
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
let mut app = App::new().await;
let mut should_quit = false;
terminal.draw(|f| ui::ui(f, &mut app))?;
while !should_quit {
if event::poll(Duration::from_millis(100))? {
if let Event::Key(key) = event::read()? {
if key.kind != KeyEventKind::Press {
continue;
}
if app.debug_keys {
app.debug_last_key = Some(format!("{:?}", key));
}
let is_ctrl = key.modifiers.contains(KeyModifiers::CONTROL);
let is_shift = key.modifiers.contains(KeyModifiers::SHIFT);
if app.show_settings_dialog {
let num_themes = app::SyntaxTheme::all().len();
match key.code {
KeyCode::Esc | KeyCode::Char('q') => {
app.show_settings_dialog = false;
continue;
}
KeyCode::Up => {
app.settings_selection = app.settings_selection.saturating_sub(1);
continue;
}
KeyCode::Down => {
if app.settings_selection < num_themes - 1 {
app.settings_selection += 1;
}
continue;
}
KeyCode::Enter => {
let themes = app::SyntaxTheme::all();
if let Some(theme) = themes.get(app.settings_selection) {
app.settings.syntax_theme = theme.clone();
let _ = utils::save_settings(&app.settings);
}
app.show_settings_dialog = false;
continue;
}
_ => continue, }
}
match (key.code, is_ctrl, is_shift) {
(KeyCode::Char('q'), true, false) => should_quit = true,
(KeyCode::Char('c'), true, false) => {
app.history.clear();
app.scroll = 0;
app.autoscroll = true;
app.save_current_model_buffers();
}
(KeyCode::Char('c'), true, true) | (KeyCode::Char('C'), true, _) => {
if let Err(e) = app.copy_selection() {
if app.debug_keys {
app.debug_last_key = Some(format!("Copy failed: {}", e));
}
}
}
(KeyCode::Char('v'), true, true) | (KeyCode::Char('V'), true, _) => {
if let Err(e) = app.paste_from_clipboard() {
if app.debug_keys {
app.debug_last_key = Some(format!("Paste failed: {}", e));
}
}
}
(KeyCode::Char('s'), true, false) => app.autoscroll = !app.autoscroll,
(KeyCode::Char('o'), true, false) => {
app.show_settings_dialog = !app.show_settings_dialog;
if app.show_settings_dialog {
app.settings_selection = 0; }
}
(KeyCode::Left, true, true) => {
app.move_cursor_word_left_with_selection();
}
(KeyCode::Right, true, true) => {
app.move_cursor_word_right_with_selection();
}
(KeyCode::Left, false, true) => {
app.move_cursor_left_with_selection();
}
(KeyCode::Right, false, true) => {
app.move_cursor_right_with_selection();
}
(KeyCode::Home, _, true) => {
app.move_cursor_home_with_selection();
}
(KeyCode::End, _, true) => {
app.move_cursor_end_with_selection();
}
(KeyCode::Left, true, false) => {
app.move_cursor_word_left();
}
(KeyCode::Right, true, false) => {
app.move_cursor_word_right();
}
(KeyCode::Home, false, false) => {
app.move_cursor_home();
}
(KeyCode::End, false, false) => {
app.move_cursor_end();
}
(KeyCode::Up, true, false) => {
app.select_previous_model();
}
(KeyCode::Down, true, false) => {
app.select_next_model();
}
(KeyCode::Up, false, false) => {
app.move_cursor_up();
}
(KeyCode::Down, false, false) => {
app.move_cursor_down();
}
(KeyCode::PageUp, _, _) => {
app.autoscroll = false;
app.scroll = app.scroll.saturating_sub(5);
}
(KeyCode::PageDown, _, _) => {
app.autoscroll = false;
app.scroll = app.scroll.saturating_add(5);
}
(KeyCode::Left, false, false) => {
app.move_cursor_left();
}
(KeyCode::Right, false, false) => {
app.move_cursor_right();
}
(KeyCode::Backspace, true, _) => {
app.delete_word_left();
}
(KeyCode::Char('h'), true, _) => {
app.delete_word_left();
}
(KeyCode::Delete, true, _) => {
app.delete_word_right();
}
(KeyCode::Delete, false, false) => {
app.delete_forward();
}
(KeyCode::Backspace, false, _) => {
app.backspace();
}
(KeyCode::Enter, _, true) => {
app.insert_char('\n');
}
(KeyCode::Enter, _, false) => {
if !app.input.is_empty() && !app.is_loading {
app.send_query(&mut terminal).await?;
}
}
(KeyCode::Char(c), false, _) => {
app.insert_char(c);
}
_ => {}
}
terminal.draw(|f| ui::ui(f, &mut app))?;
}
} else if app.is_loading {
terminal.draw(|f| ui::ui(f, &mut app))?;
} else if app.update_cursor_blink() {
terminal.draw(|f| ui::ui(f, &mut app))?;
}
}
disable_raw_mode()?;
execute!(
terminal.backend_mut(),
LeaveAlternateScreen,
DisableMouseCapture
)?;
app.save_current_model_buffers();
utils::save_history_to_file(&app.history)?;
utils::save_model_histories(&app.model_histories)?;
Ok(())
}