1#![deny(unsafe_code)]
7#![warn(rust_2018_idioms)]
8#![warn(missing_docs)]
9#![warn(clippy::all)]
10
11#[derive(Clone, Debug)]
13pub struct LineIndex {
14 line_starts: Vec<usize>,
16}
17
18impl LineIndex {
19 #[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 #[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 #[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}