use ropey::Rope;
use std::cmp::min;
pub fn right(text: &Rope, char_idx: usize) -> usize {
let len_chars = text.len_chars();
min(
if len_chars > 0 { len_chars } else { 0 },
char_idx + 1
)
}
pub fn left(char_idx: usize) -> usize {
if char_idx > 0 {
char_idx - 1
} else {
0
}
}
pub fn down(text: &Rope, char_idx: usize) -> usize {
vertical(text, char_idx, 1)
}
pub fn up(text: &Rope, char_idx: usize) -> usize {
vertical(text, char_idx, -1)
}
fn vertical(text: &Rope, char_idx: usize, lines: isize) -> usize {
let line_idx = min(text.len_lines() - 1, text.char_to_line(char_idx));
if lines < 0 && line_idx > 0 || lines > 0 && line_idx < text.len_lines() - 1 {
let x = char_idx - text.line_to_char(line_idx);
let next_line_idx = if lines > 0 {
line_idx + lines as usize
} else {
line_idx - lines.abs() as usize
};
let line_len = text.line(next_line_idx).len_chars();
text.line_to_char(next_line_idx) +
if x <= line_len {
x
} else if line_len > 0 {
line_len - 1
} else {
0
}
} else {
char_idx
}
}
pub fn line_start(text: &Rope, char_idx: usize) -> usize {
let line_idx = text.char_to_line(char_idx);
text.line_to_char(line_idx)
}
pub fn line_end(text: &Rope, char_idx: usize) -> usize {
let line_idx = text.char_to_line(char_idx);
text.line_to_char(line_idx) + if line_idx < text.len_lines() {
let line_len = text.line(line_idx).len_chars();
if line_len > 0 {
line_len - 1
} else {
0
}
} else {
0
}
}
#[cfg(test)]
mod tests {
use ropey::Rope;
const TEXT: &str = concat!(
"1st\n",
"2nd line\n",
"3rd line"
);
#[test]
fn right() {{
let text = Rope::from_str(TEXT);
let len_chars = text.len_chars();
assert_eq!(1, super::right(&text, 0));
assert_eq!(len_chars - 1, super::right(&text, len_chars - 2));
assert_eq!(len_chars, super::right(&text, len_chars - 1));
assert_eq!(len_chars, super::right(&text, len_chars));
} {
let text = Rope::from_str("");
assert_eq!(0, super::right(&text, 0));
}}
#[test]
fn left() {
assert_eq!(0, super::left(0));
assert_eq!(0, super::left(1));
assert_eq!(1, super::left(2));
}
#[test]
fn down() {{
let text = Rope::from_str(TEXT);
assert_eq!(text.line_to_char(1), super::down(&text, text.line_to_char(0)));
assert_eq!(text.line_to_char(2), super::down(&text, text.line_to_char(1)));
assert_eq!(text.line_to_char(2), super::down(&text, text.line_to_char(2)));
assert_eq!(text.line_to_char(2) + 3, super::down(&text, text.line_to_char(1) + 3));
assert_eq!(text.len_chars(), super::down(&text, text.line_to_char(2) - 1));
} {
let text = Rope::from_str("");
assert_eq!(0, super::down(&text, 0));
}}
#[test]
fn up() {{
let text = Rope::from_str(TEXT);
assert_eq!(text.line_to_char(1), super::up(&text, text.line_to_char(2)));
assert_eq!(text.line_to_char(0), super::up(&text, text.line_to_char(1)));
assert_eq!(text.line_to_char(0), super::up(&text, text.line_to_char(0)));
assert_eq!(text.line_to_char(0) + 3, super::up(&text, text.line_to_char(1) + 3));
assert_eq!(text.line_to_char(1) - 1, super::up(&text, text.line_to_char(2) - 2));
} {
let text = Rope::from_str("");
assert_eq!(0, super::up(&text, 0));
}}
#[test]
fn line_start() {{
let text = Rope::from_str(TEXT);
assert_eq!(text.line_to_char(1), super::line_start(&text, 12));
assert_eq!(text.line_to_char(1), super::line_start(&text, text.line_to_char(1)));
} {
let text = Rope::from_str("");
assert_eq!(0, super::line_start(&text, 0));
}}
#[test]
fn line_end() {{
let text = Rope::from_str(TEXT);
assert_eq!(text.line_to_char(2) - 1, super::line_end(&text, 12));
assert_eq!(text.line_to_char(2) - 1, super::line_end(&text, text.line_to_char(2) - 1));
} {
let text = Rope::from_str("");
assert_eq!(0, super::line_end(&text, 0));
}}
}