use super::text_object::Pos;
use hjkl_engine::Editor;
pub fn with_vim_buf<H: hjkl_engine::types::Host, T>(
ed: &Editor<hjkl_buffer::View, H>,
f: impl FnOnce(&VimBuf<'_>, Pos) -> T,
) -> Option<T> {
let rope = hjkl_engine::types::Query::rope(ed.buffer());
if rope.chars().all(|c| c == '\n') {
return None;
}
Some(walk_vim_buf(ed, f))
}
pub fn walk_vim_buf<H: hjkl_engine::types::Host, T>(
ed: &Editor<hjkl_buffer::View, H>,
f: impl FnOnce(&VimBuf<'_>, Pos) -> T,
) -> T {
let rope = hjkl_engine::types::Query::rope(ed.buffer());
let line_chars = |r: usize| -> Vec<char> {
let start = rope.line_to_byte(r);
rope.byte_slice(start..start + hjkl_buffer::rope_line_bytes(&rope, r))
.chars()
.collect()
};
let buf = VimBuf::new(rope.len_lines(), &line_chars);
let row = ed.cursor().0.min(buf.n_lines - 1);
let cursor = (row, ed.cursor().1.min(buf.len(row)));
f(&buf, cursor)
}
pub fn is_vim_white(c: char) -> bool {
c == ' ' || c == '\t'
}
pub struct VimBuf<'a> {
line_chars: &'a dyn Fn(usize) -> Vec<char>,
pub n_lines: usize,
line: std::cell::RefCell<Option<(usize, Vec<char>)>>,
}
impl<'a> VimBuf<'a> {
pub fn new(raw_lines: usize, line_chars: &'a dyn Fn(usize) -> Vec<char>) -> Self {
let phantom = raw_lines > 1 && line_chars(raw_lines - 1).is_empty();
Self {
line_chars,
n_lines: raw_lines - usize::from(phantom),
line: std::cell::RefCell::new(None),
}
}
fn with_line<T>(&self, row: usize, f: impl FnOnce(&[char]) -> T) -> T {
let mut line = self.line.borrow_mut();
if line.as_ref().is_none_or(|(cached, _)| *cached != row) {
*line = Some((row, (self.line_chars)(row)));
}
f(line.as_ref().map_or(&[], |(_, chars)| chars))
}
pub fn len(&self, row: usize) -> usize {
self.with_line(row, <[char]>::len)
}
pub fn gchar(&self, p: Pos) -> Option<char> {
self.with_line(p.0, |line| line.get(p.1).copied())
}
pub fn inc(&self, p: &mut Pos) -> i32 {
let len = self.len(p.0);
if p.1 < len {
p.1 += 1;
return if p.1 < len { 0 } else { 2 };
}
if p.0 + 1 < self.n_lines {
*p = (p.0 + 1, 0);
return 1;
}
-1
}
pub fn incl(&self, p: &mut Pos) -> i32 {
let mut r = self.inc(p);
if r >= 1 && p.1 != 0 {
r = self.inc(p);
}
r
}
pub fn dec(&self, p: &mut Pos) -> i32 {
if p.1 > 0 {
p.1 -= 1;
return 0;
}
if p.0 > 0 {
p.0 -= 1;
p.1 = self.len(p.0);
return 1;
}
-1
}
pub fn decl(&self, p: &mut Pos) -> i32 {
let mut r = self.dec(p);
if r == 1 && p.1 != 0 {
r = self.dec(p);
}
r
}
}