use crate::{
buffer::Buffer,
dot::{Cur, Dot, Range},
};
pub trait Find {
type Reversed: Find;
fn reversed(&self) -> Self::Reversed;
fn try_find<I>(&self, it: I) -> Option<(usize, usize)>
where
I: Iterator<Item = (usize, char)>;
fn expand(&self, dot: Dot, b: &Buffer) -> Dot
where
Self: Sized,
{
let Range {
mut start,
mut end,
start_active,
} = dot.as_range();
start = find_backward_start(self, start, b);
end = find_forward_end(self, end, b);
Dot::from(Range::from_cursors(start, end, start_active)).collapse_null_range()
}
}
pub fn find_forward<F: Find>(f: &F, cur: Cur, b: &Buffer) -> Option<Dot> {
find_between(f, cur.idx, b.txt.len_chars(), b)
}
pub fn find_forward_end<F: Find>(f: &F, cur: Cur, b: &Buffer) -> Cur {
find_forward(f, cur, b)
.unwrap_or_else(|| Cur::buffer_end(b).into())
.last_cur()
}
pub fn find_forward_wrapping<F: Find>(f: &F, b: &Buffer) -> Option<Dot> {
find_between(f, b.dot.last_cur().idx, b.txt.len_chars(), b)
.or_else(|| find_between(f, 0, b.dot.last_cur().idx, b))
}
pub fn find_backward<F: Find>(f: &F, cur: Cur, b: &Buffer) -> Option<Dot> {
rev_find_between(f, cur.idx, 0, b)
}
pub fn find_backward_start<F: Find>(f: &F, cur: Cur, b: &Buffer) -> Cur {
find_backward(f, cur, b).unwrap_or_default().first_cur()
}
fn match_to_dot(m: Option<(usize, usize)>) -> Option<Dot> {
match m {
Some((start, end)) if start == end => Some(Cur { idx: start }.into()),
Some((start, end)) => Some(Dot::from_char_indices(start, end)),
None => None,
}
}
fn find_between<F: Find>(f: &F, from: usize, to: usize, b: &Buffer) -> Option<Dot> {
match_to_dot(f.try_find(b.iter_between_chars(from, to)))
}
fn rev_find_between<F: Find>(f: &F, from: usize, to: usize, b: &Buffer) -> Option<Dot> {
let ch_from = b.txt.byte_to_char(from);
let ch_to = b.txt.byte_to_char(to);
match_to_dot(
f.reversed()
.try_find(b.rev_iter_between_chars(ch_from, ch_to)),
)
}
impl<F> Find for F
where
F: Fn(char) -> bool + Copy,
{
type Reversed = F;
fn try_find<I>(&self, it: I) -> Option<(usize, usize)>
where
I: Iterator<Item = (usize, char)>,
{
for (i, ch) in it {
if (self)(ch) {
return Some((i, i));
}
}
None
}
fn reversed(&self) -> Self::Reversed {
*self
}
}
impl Find for char {
type Reversed = char;
fn try_find<I>(&self, it: I) -> Option<(usize, usize)>
where
I: Iterator<Item = (usize, char)>,
{
for (i, ch) in it {
if ch == *self {
return Some((i, i));
}
}
None
}
fn reversed(&self) -> Self::Reversed {
*self
}
}
impl Find for &str {
type Reversed = String;
fn try_find<I>(&self, it: I) -> Option<(usize, usize)>
where
I: Iterator<Item = (usize, char)>,
{
let chars: Vec<char> = self.chars().collect();
let last = chars.len().saturating_sub(1);
let mut cix = 0;
let mut start = 0;
for (i, ch) in it {
if ch != chars[cix] {
start = 0;
cix = 0;
continue;
}
if cix == 0 {
start = i;
}
if cix == last {
return Some((start, i));
}
cix += 1;
}
None
}
fn reversed(&self) -> Self::Reversed {
self.chars().rev().collect()
}
}
impl Find for String {
type Reversed = String;
fn try_find<I>(&self, it: I) -> Option<(usize, usize)>
where
I: Iterator<Item = (usize, char)>,
{
self.as_str().try_find(it)
}
fn reversed(&self) -> Self::Reversed {
self.chars().rev().collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
use simple_test_case::test_case;
#[test_case("this"; "first word")]
#[test_case("is"; "inner word two chars")]
#[test_case("a"; "inner word single char")]
#[test_case("find"; "inner word multiple chars")]
#[test_case("test"; "last word")]
#[test]
fn find_forward_str(s: &str) {
let b = Buffer::new_virtual(0, "test", "this is a find test", Default::default());
let dot = find_forward_wrapping(&s, &b).expect("to find string");
let matched_text = dot.content(&b);
assert_eq!(matched_text, s);
}
}