Skip to main content

perl_line_index/
lib.rs

1//! Byte-oriented line/column indexing helpers.
2//!
3//! This crate has one responsibility: map byte offsets to `(line, column)`
4//! and back using cached line starts.
5
6#![deny(unsafe_code)]
7#![warn(rust_2018_idioms)]
8#![warn(missing_docs)]
9#![warn(clippy::all)]
10
11/// Line index for byte <-> (line, col) mapping.
12#[derive(Clone, Debug)]
13pub struct LineIndex {
14    /// Byte offset of each line start.
15    line_starts: Vec<usize>,
16}
17
18impl LineIndex {
19    /// Build a line index from UTF-8 text.
20    #[must_use]
21    pub fn new(text: &str) -> Self {
22        let mut line_starts = vec![0];
23        for (idx, ch) in text.char_indices() {
24            if ch == '\n' {
25                line_starts.push(idx + 1);
26            }
27        }
28        Self { line_starts }
29    }
30
31    /// Convert a byte offset to `(line, column)` using byte columns.
32    #[must_use]
33    pub fn byte_to_position(&self, byte: usize) -> (usize, usize) {
34        let line = self.line_starts.binary_search(&byte).unwrap_or_else(|i| i.saturating_sub(1));
35        let column = byte - self.line_starts[line];
36        (line, column)
37    }
38
39    /// Convert `(line, column)` back to byte offset.
40    #[must_use]
41    pub fn position_to_byte(&self, line: usize, column: usize) -> Option<usize> {
42        self.line_starts.get(line).map(|&start| start + column)
43    }
44}