use std::sync::Arc;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LineIndex {
source_name: Arc<str>,
line_starts: Vec<u32>,
}
impl LineIndex {
pub fn new(source_name: impl Into<Arc<str>>, text: &str) -> Self {
let mut line_starts = vec![0u32];
line_starts.extend(
text.bytes()
.enumerate()
.filter(|&(_, b)| b == b'\n')
.map(|(i, _)| (i + 1) as u32),
);
Self {
source_name: source_name.into(),
line_starts,
}
}
pub fn from_raw(source_name: impl Into<Arc<str>>, line_starts: Vec<u32>) -> Self {
Self {
source_name: source_name.into(),
line_starts,
}
}
pub fn source_name(&self) -> &Arc<str> {
&self.source_name
}
pub fn line_starts(&self) -> &[u32] {
&self.line_starts
}
pub fn line_col(&self, offset: usize) -> (usize, usize) {
let offset = offset as u32;
let line = match self.line_starts.binary_search(&offset) {
Ok(i) => i,
Err(i) => i.saturating_sub(1),
};
let line_start = self.line_starts.get(line).copied().unwrap_or(0);
(line + 1, (offset.saturating_sub(line_start)) as usize + 1)
}
}
#[cfg(test)]
mod tests {
use super::LineIndex;
#[test]
fn line_col_basic() {
let idx = LineIndex::new("t.rl", "let a = 1;\nlet b = 2;\nprint(a);");
assert_eq!(idx.line_col(0), (1, 1));
assert_eq!(idx.line_col(11), (2, 1));
assert_eq!(idx.line_col(15), (2, 5));
assert_eq!(idx.line_col(22), (3, 1));
}
}