mod commit;
mod emojis;
mod globals;
mod selection;
mod ui_state;
use crossterm::cursor::MoveDown;
use crossterm::event::{Event, KeyCode, KeyEventKind, read};
use crossterm::execute;
use crossterm::style::{Attribute, Print, SetAttribute, SetForegroundColor};
use crossterm::terminal::{Clear, ClearType, disable_raw_mode, enable_raw_mode};
use emojis::return_emojis;
use globals::{CATPPUCCIN_ACTIVE, cursor_to_start};
use selection::draw_menu;
use std::io;
use std::process::Command;
use unicode_segmentation::UnicodeSegmentation;
use clap::{arg, command};
use crate::commit::CommitTextType::{Description, Title};
use crate::commit::handlers::{
handle_backspace_commit, handle_char_commit, handle_left_commit, handle_right_commit,
};
use crate::commit::reload_commit_message;
use crate::selection::handlers::{
handle_backspace, handle_char, handle_enter, handle_keydown, handle_keyup, handle_left,
handle_right,
};
use crate::ui_state::UIState;
fn main() -> io::Result<()> {
let matches = command!()
.about(
r#"✨ Gitmojis, now oxidized! 🦀
When run without arguments it performs a git commit (interactive)"#,
)
.arg(arg!(-S --sign "enable signing for this specific commit"))
.get_matches();
let emojis: Vec<&'static str> = return_emojis();
let mut state = UIState {
offset: 0,
selection: 2,
user_input: String::new(),
filtered_emojis: vec![""],
insert_offset: 0,
};
state.filter_emojis(&emojis);
draw_menu(&state);
enable_raw_mode().expect("Failed to enable raw mode");
loop {
let Event::Key(event) = read()? else {
continue;
};
if event.kind != KeyEventKind::Press {
continue;
}
match event.code {
KeyCode::Down => handle_keydown(&mut state),
KeyCode::Up => handle_keyup(&mut state),
KeyCode::Left => handle_left(&mut state),
KeyCode::Right => handle_right(&mut state),
KeyCode::Char(c) => handle_char(c, &mut state, event, &emojis),
KeyCode::Backspace => handle_backspace(&mut state, &emojis),
KeyCode::Enter => {
let result = handle_enter(&state);
if result {
break;
}
}
_ => {}
}
}
let gitmoji: Vec<&str> = state.filtered_emojis[state.offset + state.selection]
.graphemes(true)
.collect();
let gitmoji = gitmoji[0].to_string();
let headline = "? Gitmoji: ".to_string() + &gitmoji + "!";
cursor_to_start();
execute!(
io::stdout(),
SetAttribute(Attribute::Bold),
SetForegroundColor(CATPPUCCIN_ACTIVE),
Print(headline),
SetAttribute(Attribute::Reset),
)
.expect("failed to print selected gitmoji");
execute!(io::stdout(), MoveDown(1)).expect("Failed to move cursor down by one line");
let mut commit_message: String = String::new();
reload_commit_message(&commit_message, state.insert_offset, false, Title);
state.insert_offset = 0;
loop {
let Event::Key(event) = read()? else {
continue;
};
if event.kind != KeyEventKind::Press {
continue;
}
match event.code {
KeyCode::Char(c) => {
handle_char_commit(c, event, &mut state, &mut commit_message, Title)
}
KeyCode::Backspace => handle_backspace_commit(&mut state, &mut commit_message, Title),
KeyCode::Left => handle_left_commit(&mut state, &commit_message, Title),
KeyCode::Right => handle_right_commit(&mut state, &commit_message, Title),
KeyCode::Enter => {
reload_commit_message(&commit_message, state.insert_offset, true, Title);
break;
}
_ => {}
}
}
let mut commit_descriptions: Vec<String> = vec![];
let mut line_count: usize = 0;
'outer: loop {
line_count += 1;
let mut commit_description: String = String::new();
state.insert_offset = 0;
execute!(io::stdout(), MoveDown(1)).expect("Failed to move cursor down one line");
reload_commit_message(
&commit_description,
state.insert_offset,
false,
Description { line_count },
);
loop {
let Event::Key(event) = read()? else {
continue;
};
if event.kind != KeyEventKind::Press {
continue;
}
match event.code {
KeyCode::Char(c) => handle_char_commit(
c,
event,
&mut state,
&mut commit_description,
Description { line_count },
),
KeyCode::Backspace => handle_backspace_commit(
&mut state,
&mut commit_description,
Description { line_count },
),
KeyCode::Left => {
handle_left_commit(&mut state, &commit_description, Description { line_count })
}
KeyCode::Right => {
handle_right_commit(&mut state, &commit_description, Description { line_count })
}
KeyCode::Enter => {
if commit_description.is_empty() {
break 'outer;
}
reload_commit_message(
&commit_description,
state.insert_offset,
true,
Description { line_count },
);
commit_descriptions.push(commit_description);
break;
}
_ => {}
}
}
}
let final_commit_message = gitmoji + " " + &commit_message;
execute!(io::stdout(), MoveDown(1)).expect("Failed to move cursor down by one line");
cursor_to_start();
disable_raw_mode().expect("Failed to disable raw mode");
let value = commit_descriptions.join("\n");
let git_args = ["commit", "-m", final_commit_message.as_str()]
.into_iter()
.chain(matches.get_flag("sign").then_some("-S"))
.chain(vec!["-m"])
.chain(vec![value.as_str()]);
Command::new("git")
.args(git_args)
.status()
.expect("Failed to run git");
Ok(())
}