use std::ops::Range;
use crate::document::{next_grapheme_offset, Buffer, Selection};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VisualKind {
Char,
Line,
}
pub fn visual_charwise_range(sel: &Selection, buf: &Buffer) -> Range<usize> {
let len = buf.len_chars();
let (lo, hi) = sel.range();
let (lo, hi) = (lo.min(len), hi.min(len));
let end = if hi < len && buf.rope().char(hi) != '\n' {
next_grapheme_offset(buf, hi)
} else {
hi
};
lo..end
}
pub fn visual_span(sel: &Selection, buf: &Buffer, kind: Option<VisualKind>) -> Range<usize> {
match kind {
Some(VisualKind::Char) => visual_charwise_range(sel, buf),
Some(VisualKind::Line) => visual_line_char_range(sel, buf),
None => {
let (lo, hi) = sel.range();
lo..hi
}
}
}
pub fn visual_line_bounds(sel: &Selection, buf: &Buffer) -> (usize, usize) {
let len = buf.len_chars();
let (start, end) = sel.range();
let first = buf.char_to_line(start.min(len));
let last = buf.char_to_line(end.min(len));
(first, last)
}
pub fn visual_line_char_range(sel: &Selection, buf: &Buffer) -> Range<usize> {
let (first, last) = visual_line_bounds(sel, buf);
let line_count = buf.line_count();
let start = buf.line_to_char(first);
let end = if last + 1 < line_count {
buf.line_to_char(last + 1)
} else {
buf.len_chars()
};
start..end
}
#[cfg(test)]
mod tests {
use super::*;
fn buf(s: &str) -> Buffer {
Buffer::from_str(s)
}
fn sel(anchor: usize, active: usize) -> Selection {
Selection { anchor, active }
}
#[test]
fn bounds_span_the_touched_lines() {
let b = buf("alpha\nbeta\ngamma\n");
assert_eq!(visual_line_bounds(&sel(2, 13), &b), (0, 2));
assert_eq!(visual_line_bounds(&sel(13, 2), &b), (0, 2));
}
#[test]
fn char_range_covers_whole_lines_with_trailing_newline() {
let b = buf("alpha\nbeta\ngamma\n");
assert_eq!(visual_line_char_range(&sel(2, 7), &b), 0..11);
}
#[test]
fn char_range_on_final_line_clamps_to_eof() {
let b = buf("alpha\nbeta");
assert_eq!(visual_line_char_range(&sel(7, 9), &b), 6..10);
}
#[test]
fn charwise_range_includes_the_char_under_the_cursor() {
let b = buf("abc\ndef");
assert_eq!(visual_charwise_range(&sel(0, 0), &b), 0..1);
assert_eq!(visual_charwise_range(&sel(0, 1), &b), 0..2);
}
#[test]
fn charwise_range_stops_at_a_line_end() {
let b = buf("abc\ndef");
assert_eq!(visual_charwise_range(&sel(0, 2), &b), 0..3);
assert_eq!(visual_charwise_range(&sel(0, 3), &b), 0..3);
}
#[test]
fn charwise_range_at_end_of_buffer_does_not_overrun() {
let b = buf("abc");
assert_eq!(visual_charwise_range(&sel(0, 3), &b), 0..3);
assert_eq!(visual_charwise_range(&sel(0, 9), &b), 0..3);
}
#[test]
fn charwise_range_extends_the_high_end_of_a_reverse_selection() {
let b = buf("abcd");
assert_eq!(visual_charwise_range(&sel(2, 0), &b), 0..3);
}
#[test]
fn charwise_range_extends_by_a_whole_grapheme() {
let b = buf("e\u{301}x");
assert_eq!(visual_charwise_range(&sel(0, 0), &b), 0..2);
}
#[test]
fn visual_span_dispatches_on_kind() {
let b = buf("alpha\nbeta\n");
let s = sel(0, 2);
assert_eq!(visual_span(&s, &b, Some(VisualKind::Char)), 0..3);
assert_eq!(visual_span(&s, &b, Some(VisualKind::Line)), 0..6);
assert_eq!(visual_span(&s, &b, None), 0..2);
}
}