use crate::{
buffer::Buffer,
mode::Mode,
motion::Motion,
register::RegisterHandler,
text_object::{TextObject, TextObjectType},
undo::{Action, UndoTree},
};
pub struct Operator {
pub name: char,
command: fn(
end: usize,
buffer: &mut Buffer,
register_handler: &mut RegisterHandler,
mode: &mut Mode,
undo_tree: &mut UndoTree,
),
}
impl Operator {
pub fn new(
name: char,
command: fn(
end: usize,
buffer: &mut Buffer,
register_handler: &mut RegisterHandler,
mode: &mut Mode,
undo_tree: &mut UndoTree,
),
) -> Self {
Self { name, command }
}
pub fn execute_motion(
&self,
motion: &Motion,
buffer: &mut Buffer,
register_handler: &mut RegisterHandler,
mode: &mut Mode,
undo_tree: &mut UndoTree,
) {
let mut end = motion.evaluate(buffer);
if !motion.inclusive && end > buffer.cursor {
end = usize::max(end, 1) - 1;
}
(self.command)(end, buffer, register_handler, mode, undo_tree);
}
pub fn execute_text_object(
&self,
text_object: &TextObject,
text_object_type: &TextObjectType,
buffer: &mut Buffer,
register_handler: &mut RegisterHandler,
mode: &mut Mode,
undo_tree: &mut UndoTree,
) {
let Some((beginning, end)) = (match text_object_type {
TextObjectType::Inside => text_object.inside(buffer),
TextObjectType::Around => text_object.around(buffer),
}) else {
return;
};
buffer.cursor = beginning;
(self.command)(end, buffer, register_handler, mode, undo_tree);
}
pub fn entire_line(
&self,
buffer: &mut Buffer,
register_handler: &mut RegisterHandler,
mode: &mut Mode,
undo_tree: &mut UndoTree,
) {
buffer.start_of_line();
let end_of_line = buffer.get_end_of_line();
let reg_before = register_handler.get_reg().to_string();
(self.command)(end_of_line, buffer, register_handler, mode, undo_tree);
if reg_before != register_handler.get_reg()
&& register_handler.get_reg().chars().last().unwrap_or('\n') != '\n'
{
register_handler.push_reg(&'\n');
}
}
}
#[allow(clippy::too_many_arguments)]
pub fn iterate_range(
mode: &mut Mode,
register_handler: &mut RegisterHandler,
buffer: &mut Buffer,
end: usize,
initial_callback: fn(
register_handler: &mut RegisterHandler,
buffer: &mut Buffer,
mode: &mut Mode,
),
iter_callback: fn(register_handler: &mut RegisterHandler, buffer: &mut Buffer),
after_callback: fn(
start: usize,
register_handler: &mut RegisterHandler,
buffer: &mut Buffer,
mode: &mut Mode,
),
will_delete: bool,
) {
let anchor = buffer.cursor;
let count = try_into_or_return!(i32, end) - try_into_or_return!(i32, anchor);
initial_callback(register_handler, buffer, mode);
let initial_register_contents = register_handler.get_reg().to_string();
if let 1.. = count {
if will_delete {
(0..=count).for_each(|_| iter_callback(register_handler, buffer));
} else {
(0..=count).for_each(|_| {
iter_callback(register_handler, buffer);
if buffer.cursor + 1 < buffer.rope.len_chars() {
buffer.next_char();
}
});
}
} else {
(0..=count.abs()).for_each(|_| {
iter_callback(register_handler, buffer);
});
let final_register_contents = register_handler.get_reg();
if initial_register_contents != final_register_contents {
register_handler.set_reg(final_register_contents.chars().rev().collect::<String>());
}
}
after_callback(anchor, register_handler, buffer, mode);
}
const fn noop(
_start: usize,
_register_handler: &mut RegisterHandler,
_buffer: &mut Buffer,
_mode: &mut Mode,
) {
}
const fn reset_position(
start: usize,
_register_handler: &mut RegisterHandler,
buffer: &mut Buffer,
_mode: &mut Mode,
) {
buffer.cursor = start;
}
fn insert(
_start: usize,
_register_handler: &mut RegisterHandler,
_buffer: &mut Buffer,
mode: &mut Mode,
) {
mode.insert();
}
fn clear_reg(register_handler: &mut RegisterHandler, _buffer: &mut Buffer, _mode: &mut Mode) {
register_handler.empty_reg();
}
fn delete_char(register_handler: &mut RegisterHandler, buffer: &mut Buffer) {
if buffer.rope.len_chars() <= buffer.cursor {
return;
}
register_handler.push_reg(&buffer.get_curr_char());
buffer.delete_curr_char();
}
pub fn delete(
end: usize,
buffer: &mut Buffer,
register_handler: &mut RegisterHandler,
mode: &mut Mode,
undo_tree: &mut UndoTree,
) {
iterate_range(
mode,
register_handler,
buffer,
end,
clear_reg,
delete_char,
noop,
true,
);
buffer.update_list_use_current_line();
let text = register_handler.get_reg();
let action = Action::delete(buffer.cursor, &text);
undo_tree.new_action(action);
buffer.clamp_cursor();
}
fn yank_char(register_handler: &mut RegisterHandler, buffer: &mut Buffer) {
register_handler.push_reg(&buffer.get_curr_char());
}
pub fn yank(
end: usize,
buffer: &mut Buffer,
register_handler: &mut RegisterHandler,
mode: &mut Mode,
_undo_tree: &mut UndoTree,
) {
if end == buffer.rope.len_chars() && end == buffer.cursor {
return;
}
iterate_range(
mode,
register_handler,
buffer,
end,
clear_reg,
yank_char,
reset_position,
false,
);
buffer.clamp_cursor();
}
pub fn change(
end: usize,
buffer: &mut Buffer,
register_handler: &mut RegisterHandler,
mode: &mut Mode,
undo_tree: &mut UndoTree,
) {
iterate_range(
mode,
register_handler,
buffer,
end,
clear_reg,
delete_char,
insert,
true,
);
buffer.update_list_use_current_line();
let text = register_handler.get_reg();
let action = Action::delete(buffer.cursor, &text);
undo_tree.new_action(action);
buffer.clamp_cursor();
}