1use crate::span::Span;
2
3#[derive(Debug, Clone)]
4pub struct LineIndex {
5 line_starts: Vec<usize>,
7 len: usize,
8}
9
10impl LineIndex {
11 pub fn new(source: &[u8]) -> Self {
12 let mut line_starts = vec![0];
13 for (i, &b) in source.iter().enumerate() {
14 if b == b'\n' {
15 line_starts.push(i + 1);
16 }
17 }
18 Self {
19 line_starts,
20 len: source.len(),
21 }
22 }
23
24 pub fn line_col(&self, offset: usize) -> (usize, usize) {
27 if offset > self.len {
28 let last_line = self.line_starts.len() - 1;
30 let last_start = self.line_starts[last_line];
31 return (last_line, self.len.saturating_sub(last_start));
32 }
33
34 match self.line_starts.binary_search(&offset) {
36 Ok(line) => (line, 0),
37 Err(insert_idx) => {
38 let line = insert_idx - 1;
39 let col = offset - self.line_starts[line];
40 (line, col)
41 }
42 }
43 }
44
45 pub fn offset(&self, line: usize, col: usize) -> Option<usize> {
48 if line >= self.line_starts.len() {
49 return None;
50 }
51 let start = self.line_starts[line];
52 let offset = start + col;
53
54 if line + 1 < self.line_starts.len() && offset >= self.line_starts[line + 1] {
58 }
64
65 if offset > self.len {
66 None
67 } else {
68 Some(offset)
69 }
70 }
71
72 pub fn to_lsp_range(&self, span: Span) -> (usize, usize, usize, usize) {
73 let (start_line, start_col) = self.line_col(span.start);
74 let (end_line, end_col) = self.line_col(span.end);
75 (start_line, start_col, end_line, end_col)
76 }
77}