pub fn rope_line_to_str(rope: &ropey::Rope, r: usize) -> String {
let s = rope.line(r).to_string();
if s.ends_with('\n') {
s[..s.len() - 1].to_string()
} else {
s
}
}
pub fn rope_row_range_str(rope: &ropey::Rope, lo: usize, hi: usize) -> String {
let n = rope.len_lines();
let lo = lo.min(n.saturating_sub(1));
let hi = hi.min(n.saturating_sub(1));
if lo > hi {
return String::new();
}
let start_byte = rope.line_to_byte(lo);
let end_byte = if hi + 1 < n {
rope.line_to_byte(hi + 1).saturating_sub(1)
} else {
rope.len_bytes()
};
rope.byte_slice(start_byte..end_byte).to_string()
}
pub fn rope_to_lines_vec(rope: &ropey::Rope) -> Vec<String> {
let n = rope.len_lines();
(0..n).map(|r| rope_line_to_str(rope, r)).collect()
}
#[cfg(test)]
mod tests {
use super::*;
use ropey::Rope;
#[test]
fn line_to_str_strips_newline() {
let rope = Rope::from_str("abc\ndef\n");
assert_eq!(rope_line_to_str(&rope, 0), "abc");
assert_eq!(rope_line_to_str(&rope, 1), "def");
}
#[test]
fn line_to_str_final_line_without_newline() {
let rope = Rope::from_str("abc\ndef");
assert_eq!(rope_line_to_str(&rope, 1), "def");
}
#[test]
fn row_range_str_joins_inclusive() {
let rope = Rope::from_str("a\nb\nc\n");
assert_eq!(rope_row_range_str(&rope, 0, 1), "a\nb");
assert_eq!(rope_row_range_str(&rope, 1, 2), "b\nc");
}
#[test]
fn row_range_str_single_row() {
let rope = Rope::from_str("a\nb\nc");
assert_eq!(rope_row_range_str(&rope, 1, 1), "b");
}
#[test]
fn row_range_str_clamps_out_of_bounds() {
let rope = Rope::from_str("a\nb");
assert_eq!(rope_row_range_str(&rope, 0, 99), "a\nb");
}
#[test]
fn row_range_str_empty_when_lo_gt_hi() {
let rope = Rope::from_str("a\nb\nc");
assert_eq!(rope_row_range_str(&rope, 2, 1), "");
}
#[test]
fn to_lines_vec_snapshots_all_rows() {
let rope = Rope::from_str("a\nb\nc");
assert_eq!(rope_to_lines_vec(&rope), vec!["a", "b", "c"]);
}
}