use crate::{
action::match_action, commands::Command as Cmd, count::update_count, global_state::GlobalState,
meta_command::match_meta_command, mode::Mode, motion::Motion, operator::Operator,
text_object::TextObject, undo::Action, view::View, view_command::ViewCommand,
};
use anyhow::Result;
use crossterm::event::{Event, KeyCode, read};
#[allow(clippy::too_many_arguments)]
#[allow(clippy::too_many_lines)]
pub fn program_loop<'a>(
commands: &[Cmd],
operators: &'a [Operator],
motions: &[Motion],
text_objects: &[TextObject],
view_commands: &[ViewCommand],
all_normal_chars: &[char],
mut global_state: GlobalState<'a>,
mut view: View,
) -> Result<()> {
let mut last_count = 1;
let mut last_chained: Vec<char> = vec![];
'main: loop {
let buffer = view.get_buffer_mut();
buffer.update_list_reset();
let Event::Key(event) = read()? else { continue };
match (event.code, global_state.mode.clone()) {
(KeyCode::Char(c), Mode::Normal) if c.is_numeric() => {
update_count(c, &mut global_state.count);
}
(KeyCode::Char(':'), Mode::Normal) => {
global_state.mode = Mode::Meta;
global_state.status_bar.push(':');
}
(KeyCode::Char('/'), Mode::Normal) => {
global_state.mode.search();
global_state.status_bar.push('/');
}
(KeyCode::Char('n'), Mode::Normal) => {
buffer.goto_next_string(&global_state.search_str);
}
(KeyCode::Char('N'), Mode::Normal) => {
buffer.goto_prev_string(&global_state.search_str);
}
(KeyCode::Char('.'), Mode::Normal) => match_action(
&mut global_state,
&mut last_chained,
&mut last_count,
&mut view,
commands,
operators,
motions,
text_objects,
view_commands,
),
(KeyCode::Char(c), Mode::Normal) => {
if !all_normal_chars.contains(&c) {
continue;
}
global_state.chained.push(c);
match_action(
&mut global_state,
&mut last_chained,
&mut last_count,
&mut view,
commands,
operators,
motions,
text_objects,
view_commands,
);
}
(KeyCode::Esc, Mode::Normal) => {
global_state.chained.clear();
global_state.count = 1;
global_state.next_operation = None;
}
(KeyCode::Esc, Mode::Insert) => {
if buffer.cursor != buffer.get_start_of_line() {
buffer.cursor -= 1;
}
global_state.mode.normal();
}
(KeyCode::Backspace, Mode::Insert) => {
buffer.backspace(&mut global_state.undo_tree);
}
(KeyCode::Char(c), Mode::Insert) => {
buffer.insert_char(c);
buffer.cursor += 1;
buffer.update_list_use_current_line();
let action = Action::insert(buffer.cursor - 1, &c);
global_state.undo_tree.new_action_merge(action);
}
(KeyCode::Tab, Mode::Insert) => {
buffer.insert_n_times(' ', 4);
buffer.cursor += 4;
buffer.update_list_use_current_line();
}
(KeyCode::Enter, Mode::Insert) => {
let newline = buffer.insert_newline();
let action = Action::insert(buffer.cursor - newline.len(), &newline);
global_state.undo_tree.new_action(action);
}
(KeyCode::Enter, Mode::Meta) => {
if match_meta_command(&mut global_state, &mut view)? {
break 'main;
}
}
(KeyCode::Char(c), Mode::Meta | Mode::Search) => {
global_state.status_bar.push(c);
}
(KeyCode::Esc, Mode::Meta | Mode::Search) => {
global_state.mode.normal();
global_state.status_bar.clear();
}
(KeyCode::Backspace, Mode::Meta | Mode::Search) => {
global_state.status_bar.delete();
}
(_, Mode::Meta) => {}
(KeyCode::Enter, Mode::Search) => {
global_state.search_str = global_state
.status_bar
.buffer()
.split_at(1)
.1
.chars()
.collect();
global_state.mode.normal();
global_state.status_bar.clear();
}
(KeyCode::Left, Mode::Normal | Mode::Insert) => buffer.prev_char(),
(KeyCode::Right, Mode::Normal | Mode::Insert) => buffer.next_char(),
(KeyCode::Up, Mode::Normal | Mode::Insert) => buffer.prev_row(),
(KeyCode::Down, Mode::Normal | Mode::Insert) => buffer.next_row(),
_ => continue,
}
let _ = view.get_view_box().parse();
let adjusted = view.adjust();
view.flush(&global_state, adjusted)?;
}
Ok(())
}