use crate::{
buffer::Buffer,
utility::{is_symbol, on_next_input},
};
use crossterm::event::KeyCode;
pub struct Motion {
pub name: char,
command: fn(buffer: &mut Buffer),
pub inclusive: bool,
}
impl Motion {
pub fn exclusive(name: char, command: fn(buffer: &mut Buffer)) -> Self {
Self {
name,
command,
inclusive: false,
}
}
pub fn inclusive(name: char, 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
}
}
impl Buffer {
pub fn word(buffer: &mut Self) {
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 Self) {
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 Self) {
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());
}
}
}
}
}
impl Buffer {
fn find_generic(&mut self, key: KeyCode, traverse: impl Fn(&Self, char) -> Option<usize>) {
if let KeyCode::Char(target) = key
&& let Some(position) = traverse(self, target)
{
self.set_cursor(position);
}
}
pub fn find(buffer: &mut Self) {
let find_forward =
|key: KeyCode, buffer: &mut Self| buffer.find_generic(key, Self::find_next);
on_next_input(buffer, find_forward).expect("Failed to get character to find");
}
pub fn find_until(buffer: &mut Self) {
let find_until = |key: KeyCode, buffer: &mut Self| {
buffer.find_generic(key, Self::find_next);
if buffer.cursor != 0 {
buffer.cursor -= 1;
}
};
on_next_input(buffer, find_until).expect("Failed to get character to find");
}
pub fn find_back(buffer: &mut Self) {
let find_back = |key: KeyCode, buffer: &mut Self| buffer.find_generic(key, Self::find_prev);
on_next_input(buffer, find_back).expect("Failed to get character to find");
}
}
impl Buffer {
fn find_from_list_on_line(&mut self, list: &[char]) -> Option<char> {
let anchor = self.cursor;
let end_of_line = self.get_end_of_line();
let mut c = self.get_curr_char();
while !list.contains(&c) && end_of_line > self.cursor + 1 {
self.cursor += 1;
c = self.get_curr_char();
}
if list.contains(&c) {
return Some(c);
}
self.set_cursor(anchor);
None
}
fn search_matching(
&mut self,
start_character: char,
search_character: char,
direction: i32,
exit_condition: impl Fn(usize) -> bool,
anchor: usize,
) {
let mut count = 0;
let mut c = self.get_curr_char();
if c != start_character {
return;
}
loop {
if exit_condition(self.cursor) {
self.set_cursor(anchor);
return;
}
let cursor = try_into_or_return!(i32, self.cursor);
self.cursor = try_into_or_return!(usize, cursor + direction);
c = self.get_curr_char();
if c == start_character {
count += 1;
} else if c == search_character {
if count == 0 {
return;
}
count -= 1;
}
}
}
pub fn next_corresponding_bracket(&mut self) {
let anchor = self.cursor;
let Some(c) = self.find_from_list_on_line(&['{', '}', '[', ']', '(', ')']) else {
return;
};
let end_of_file = self.rope.len_chars();
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 forward_check = |cursor: usize| -> bool { end_of_file == cursor + 1 };
let backward_check = |cursor: usize| -> bool { cursor == 0 };
match direction {
1 => self.search_matching(
start_character,
search_character,
direction,
forward_check,
anchor,
),
-1 => self.search_matching(
start_character,
search_character,
direction,
backward_check,
anchor,
),
_ => unreachable!(),
}
}
}
impl Buffer {
fn generic_empty_line(
&mut self,
is_at_end: impl Fn(&Self) -> bool,
traverse: impl Fn(&mut Self),
) {
while self.is_empty_line() {
if is_at_end(self) {
return;
}
traverse(self);
}
while !self.is_empty_line() {
if is_at_end(self) {
return;
}
traverse(self);
}
}
pub fn next_empty_line(&mut self) {
self.generic_empty_line(Self::is_last_row, Self::next_row);
}
pub fn prev_empty_line(&mut self) {
self.generic_empty_line(Self::is_first_row, Self::prev_row);
}
}
impl Buffer {
pub fn beginning_of_line(buffer: &mut Self) {
let col = buffer.get_first_non_whitespace_col().unwrap_or(0);
buffer.set_col(col);
}
}