use crate::{
buffer::Buffer,
utility::{is_symbol, on_next_input_buffer_only},
};
use crossterm::event::KeyCode;
pub struct Motion<'a> {
pub name: &'a str,
command: fn(buffer: &mut Buffer),
pub inclusive: bool,
}
impl<'a> Motion<'a> {
pub fn exclusive(name: &'a str, command: fn(buffer: &mut Buffer)) -> Self {
Self {
name,
command,
inclusive: false,
}
}
pub fn inclusive(name: &'a str, command: fn(buffer: &mut Buffer)) -> Self {
Self {
name,
command,
inclusive: true,
}
}
pub fn apply(&self, buffer: &mut Buffer) {
(self.command)(buffer);
}
pub fn evaluate(&self, buffer: &Buffer) -> usize {
let mut fake_buffer = buffer.clone();
(self.command)(&mut fake_buffer);
fake_buffer.cursor
}
}
pub fn prev_char(buffer: &mut Buffer) {
buffer.prev_char();
}
pub fn next_row(buffer: &mut Buffer) {
if buffer.is_last_row() {
return;
}
buffer.next_line();
}
pub fn prev_row(buffer: &mut Buffer) {
if buffer.get_row() == 0 {
return;
}
buffer.prev_line();
let len = buffer.get_curr_line().len_chars();
let col = if len > 0 {
usize::min(buffer.get_col(), len - 1)
} else {
0
};
buffer.set_col(col);
}
pub fn next_char(buffer: &mut Buffer) {
buffer.next_char();
}
pub fn word(buffer: &mut Buffer) {
if buffer.get_curr_line().len_chars() == buffer.get_col() {
return;
}
let mut c = buffer.get_curr_char();
let last_legal_char = buffer.rope.len_chars() - 1;
if is_symbol(c) {
while is_symbol(c) && buffer.cursor < last_legal_char {
c = unwrap_or_break!(buffer.next_and_char());
}
} else if c.is_alphanumeric() || c == '_' {
while (c.is_alphanumeric() || c == '_') && buffer.cursor < last_legal_char {
c = unwrap_or_break!(buffer.next_and_char());
}
}
while c.is_whitespace() && buffer.cursor < last_legal_char {
if c == '\n' {
if buffer.cursor < last_legal_char {
buffer.cursor += 1;
}
return;
}
c = unwrap_or_break!(buffer.next_and_char());
}
}
pub fn back(buffer: &mut Buffer) {
if buffer.cursor == 0 {
return;
}
let mut prev_char = unwrap_or_return!(buffer.get_prev_char());
if prev_char == '\n' && buffer.cursor > 0 {
buffer.cursor -= 1;
return;
}
if is_symbol(prev_char) {
while is_symbol(prev_char) && buffer.cursor > 0 {
buffer.prev_char();
prev_char = unwrap_or_break!(buffer.get_prev_char());
}
} else if prev_char.is_alphanumeric() || prev_char == '_' {
while prev_char.is_alphanumeric() || prev_char == '_' && buffer.cursor > 0 {
buffer.prev_char();
prev_char = unwrap_or_break!(buffer.get_prev_char());
}
} else {
while prev_char.is_whitespace() && buffer.cursor > 0 {
buffer.prev_char();
prev_char = unwrap_or_break!(buffer.get_prev_char());
}
if prev_char.is_alphanumeric() {
while prev_char.is_alphanumeric() && buffer.cursor > 0 {
buffer.prev_char();
prev_char = unwrap_or_break!(buffer.get_prev_char());
}
} else if is_symbol(prev_char) {
while is_symbol(prev_char) && buffer.cursor > 0 {
buffer.prev_char();
prev_char = unwrap_or_break!(buffer.get_prev_char());
}
}
}
}
pub fn end_of_word(buffer: &mut Buffer) {
if buffer.get_curr_line().len_chars() == buffer.get_col() {
return;
}
let last_legal_char = buffer.rope.len_chars() - 1;
let mut next_char = unwrap_or_return!(buffer.get_next_char());
if next_char == '\n' {
if buffer.cursor < last_legal_char {
buffer.cursor += 2;
}
return;
}
if is_symbol(next_char) {
while is_symbol(next_char) && buffer.cursor < last_legal_char {
buffer.next_char();
next_char = unwrap_or_break!(buffer.get_next_char());
}
} else if next_char.is_alphanumeric() || next_char == '_' {
while next_char.is_alphanumeric() || next_char == '_' && buffer.cursor < last_legal_char {
buffer.next_char();
next_char = unwrap_or_break!(buffer.get_next_char());
}
} else {
while next_char.is_whitespace() && buffer.cursor < last_legal_char {
buffer.next_char();
next_char = unwrap_or_break!(buffer.get_next_char());
}
if next_char.is_alphanumeric() {
while next_char.is_alphanumeric() && buffer.cursor < last_legal_char {
buffer.next_char();
next_char = unwrap_or_break!(buffer.get_next_char());
}
} else if is_symbol(next_char) {
while is_symbol(next_char) && buffer.cursor < last_legal_char {
buffer.next_char();
next_char = unwrap_or_break!(buffer.get_next_char());
}
}
}
}
pub fn end_of_line(buffer: &mut Buffer) {
buffer.end_of_line();
}
pub fn beginning_of_line(buffer: &mut Buffer) {
buffer.set_col(buffer.first_non_whitespace_col());
}
pub fn find(buffer: &mut Buffer) {
fn find(key: KeyCode, buffer: &mut Buffer) {
if let KeyCode::Char(target) = key {
let anchor = buffer.cursor;
loop {
if buffer.cursor == buffer.get_end_of_line() {
break;
}
if buffer.get_curr_char() == target {
return;
}
buffer.cursor += 1;
}
buffer.cursor = anchor;
}
}
on_next_input_buffer_only(buffer, find).expect("Failed to get character to find");
}
pub fn find_until(buffer: &mut Buffer) {
fn find_until(key: KeyCode, buffer: &mut Buffer) {
if let KeyCode::Char(target) = key {
let anchor = buffer.cursor;
loop {
if buffer.cursor == buffer.get_end_of_line() {
break;
}
if buffer.get_curr_char() == target {
if buffer.cursor != 0 {
buffer.cursor -= 1;
}
return;
}
buffer.cursor += 1;
}
buffer.cursor = anchor;
}
}
on_next_input_buffer_only(buffer, find_until).expect("Failed to get character to find");
}
pub fn find_back(buffer: &mut Buffer) {
fn find_back(key: KeyCode, buffer: &mut Buffer) {
if let KeyCode::Char(target) = key {
let anchor = buffer.cursor;
loop {
if buffer.cursor == 0 {
break;
}
if buffer.get_curr_char() == target {
return;
}
buffer.cursor -= 1;
}
buffer.cursor = anchor;
}
}
on_next_input_buffer_only(buffer, find_back).expect("Failed to get character to find");
}
pub fn next_corresponding_bracket(buffer: &mut Buffer) {
let anchor = buffer.cursor;
let end_of_line = buffer.get_end_of_line();
let mut c = buffer.get_curr_char();
while c != '{' && c != '[' && c != '(' && c != ')' && c != ']' && c != '}' {
if end_of_line > buffer.cursor {
buffer.cursor += 1;
c = buffer.get_curr_char();
} else {
buffer.cursor = anchor;
return;
}
}
let end_of_file = buffer.rope.len_chars();
let mut count = 0;
let start_character = c;
let (search_character, direction): (char, i32) = match c {
'{' => ('}', 1),
'[' => (']', 1),
'(' => (')', 1),
'}' => ('{', -1),
']' => ('[', -1),
')' => ('(', -1),
_ => panic!("Bug in next_bracket function"),
};
let condition: Box<dyn Fn(usize) -> bool> = match direction {
1 => Box::new(|cursor: usize| end_of_file > cursor + 1),
-1 => Box::new(|cursor: usize| i32::try_from(cursor).unwrap() > 0),
_ => unreachable!(),
};
loop {
if condition(buffer.cursor) {
buffer.cursor =
usize::try_from(i32::try_from(buffer.cursor).unwrap() + direction).unwrap();
c = buffer.get_curr_char();
} else {
buffer.cursor = anchor;
return;
}
if c == start_character {
count += 1;
} else if c == search_character {
if count == 0 {
break;
}
count -= 1;
}
}
}
pub fn next_newline(buffer: &mut Buffer) {
while buffer.is_empty_line() {
if buffer.is_last_row() {
return;
}
next_row(buffer);
}
while !buffer.is_empty_line() {
if buffer.is_last_row() {
return;
}
next_row(buffer);
}
}
pub fn prev_newline(buffer: &mut Buffer) {
while buffer.is_empty_line() {
if buffer.is_first_row() {
return;
}
prev_row(buffer);
}
while !buffer.is_empty_line() {
if buffer.is_first_row() {
return;
}
prev_row(buffer);
}
}