use super::BytePos;
pub struct LineOffsets {
offsets: Vec<usize>,
len: usize,
}
impl LineOffsets {
pub fn new(data: &str) -> Self {
let mut offsets = vec![0];
let len = data.len();
for (i, val) in data.bytes().enumerate() {
if val == b'\n' {
offsets.push(i + 1);
}
}
Self { offsets, len }
}
pub fn line(&self, pos: BytePos) -> usize {
let offset = pos.0;
assert!(offset <= self.len);
match self.offsets.binary_search(&offset) {
Ok(line) => line + 1,
Err(line) => line,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_string() {
let offsets = LineOffsets::new("");
assert_eq!(offsets.line(BytePos(0)), 1);
}
#[test]
fn test_single_line() {
let offsets = LineOffsets::new("hello world");
assert_eq!(offsets.line(BytePos(0)), 1);
assert_eq!(offsets.line(BytePos(5)), 1);
assert_eq!(offsets.line(BytePos(10)), 1);
}
#[test]
fn test_multiple_lines() {
let offsets = LineOffsets::new("line1\nline2\nline3");
assert_eq!(offsets.line(BytePos(0)), 1); assert_eq!(offsets.line(BytePos(5)), 1); assert_eq!(offsets.line(BytePos(6)), 2); assert_eq!(offsets.line(BytePos(11)), 2); assert_eq!(offsets.line(BytePos(12)), 3); assert_eq!(offsets.line(BytePos(16)), 3); }
#[test]
fn test_trailing_newline() {
let offsets = LineOffsets::new("hello\n");
assert_eq!(offsets.line(BytePos(0)), 1);
assert_eq!(offsets.line(BytePos(5)), 1);
assert_eq!(offsets.line(BytePos(6)), 2);
}
#[test]
fn test_multiple_consecutive_newlines() {
let offsets = LineOffsets::new("a\n\n\nb");
assert_eq!(offsets.line(BytePos(0)), 1); assert_eq!(offsets.line(BytePos(2)), 2); assert_eq!(offsets.line(BytePos(3)), 3); assert_eq!(offsets.line(BytePos(4)), 4); }
#[test]
fn test_different_line_endings() {
let offsets = LineOffsets::new("line1\r\nline2\nline3");
assert_eq!(offsets.line(BytePos(0)), 1); assert_eq!(offsets.line(BytePos(6)), 1); assert_eq!(offsets.line(BytePos(7)), 2); assert_eq!(offsets.line(BytePos(12)), 2); assert_eq!(offsets.line(BytePos(13)), 3); }
#[test]
#[should_panic]
fn test_position_beyond_length() {
let offsets = LineOffsets::new("hello");
offsets.line(BytePos(10)); }
}