mod app;
mod error;
mod git;
mod handler;
mod input;
mod model;
mod output;
mod persistence;
mod syntax;
mod ui;
use std::io;
use std::time::Duration;
use crossterm::{
event::{
self, Event, KeyboardEnhancementFlags, PopKeyboardEnhancementFlags,
PushKeyboardEnhancementFlags,
},
execute,
terminal::{
EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode,
supports_keyboard_enhancement,
},
};
use ratatui::{Terminal, backend::CrosstermBackend};
use app::{App, FocusedPanel, InputMode};
use handler::{
handle_command_action, handle_comment_action, handle_commit_select_action,
handle_confirm_action, handle_diff_action, handle_file_list_action, handle_help_action,
};
use input::{Action, map_key_to_action};
fn main() -> anyhow::Result<()> {
let original_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |panic_info| {
let _ = execute!(io::stdout(), PopKeyboardEnhancementFlags);
let _ = disable_raw_mode();
let _ = execute!(io::stdout(), LeaveAlternateScreen);
original_hook(panic_info);
}));
let keyboard_enhancement_supported = matches!(supports_keyboard_enhancement(), Ok(true));
let mut app = match App::new() {
Ok(mut app) => {
app.supports_keyboard_enhancement = keyboard_enhancement_supported;
app
}
Err(e) => {
eprintln!("Error: {}", e);
eprintln!("\nMake sure you're in a git repository with uncommitted changes.");
std::process::exit(1);
}
};
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen)?;
if keyboard_enhancement_supported {
let _ = execute!(
stdout,
PushKeyboardEnhancementFlags(KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES)
);
}
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
let mut pending_z = false;
let mut pending_d = false;
let mut pending_semicolon = false;
loop {
terminal.draw(|frame| {
ui::render(frame, &mut app);
})?;
if event::poll(Duration::from_millis(100))?
&& let Event::Key(key) = event::read()?
{
if pending_z {
pending_z = false;
if key.code == crossterm::event::KeyCode::Char('z') {
app.center_cursor();
continue;
}
}
if pending_d {
pending_d = false;
if key.code == crossterm::event::KeyCode::Char('d') {
if !app.delete_comment_at_cursor() {
app.set_message("No comment at cursor");
}
continue;
}
}
if pending_semicolon {
pending_semicolon = false;
match key.code {
crossterm::event::KeyCode::Char('e') => {
app.toggle_file_list();
continue;
}
crossterm::event::KeyCode::Char('h') => {
app.focused_panel = app::FocusedPanel::FileList;
continue;
}
crossterm::event::KeyCode::Char('l') => {
app.focused_panel = app::FocusedPanel::Diff;
continue;
}
_ => {}
}
}
let action = map_key_to_action(key, app.input_mode);
match action {
Action::PendingZCommand => {
pending_z = true;
continue;
}
Action::PendingDCommand => {
pending_d = true;
continue;
}
Action::PendingSemicolonCommand => {
pending_semicolon = true;
continue;
}
_ => {}
}
match app.input_mode {
InputMode::Help => handle_help_action(&mut app, action),
InputMode::Command => handle_command_action(&mut app, action),
InputMode::Comment => handle_comment_action(&mut app, action),
InputMode::Confirm => handle_confirm_action(&mut app, action),
InputMode::CommitSelect => handle_commit_select_action(&mut app, action),
InputMode::Normal => match app.focused_panel {
FocusedPanel::FileList => handle_file_list_action(&mut app, action),
FocusedPanel::Diff => handle_diff_action(&mut app, action),
},
}
}
if app.should_quit {
break;
}
}
let _ = execute!(terminal.backend_mut(), PopKeyboardEnhancementFlags);
disable_raw_mode()?;
execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
Ok(())
}