aboutsummaryrefslogtreecommitdiff
path: root/crates/libeditor/src/line_index.rs
blob: 801726aa59b953df5266c620c6cc5d32786f5fbf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
use superslice::Ext;
use ::TextUnit;

#[derive(Clone, Debug)]
pub struct LineIndex {
    newlines: Vec<TextUnit>,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct LineCol {
    pub line: u32,
    pub col: TextUnit,
}

impl LineIndex {
    pub fn new(text: &str) -> LineIndex {
        let mut newlines = vec![0.into()];
        let mut curr = 0.into();
        for c in text.chars() {
            curr += TextUnit::of_char(c);
            if c == '\n' {
                newlines.push(curr);
            }
        }
        LineIndex { newlines }
    }

    pub fn line_col(&self, offset: TextUnit) -> LineCol {
        let line = self.newlines.upper_bound(&offset) - 1;
        let line_start_offset = self.newlines[line];
        let col = offset - line_start_offset;
        return LineCol { line: line as u32, col };
    }

    pub fn offset(&self, line_col: LineCol) -> TextUnit {
        //TODO: return Result
        self.newlines[line_col.line as usize] + line_col.col
    }
}

#[test]
fn test_line_index() {
    let text = "hello\nworld";
    let index = LineIndex::new(text);
    assert_eq!(index.line_col(0.into()), LineCol { line: 0, col: 0.into() });
    assert_eq!(index.line_col(1.into()), LineCol { line: 0, col: 1.into() });
    assert_eq!(index.line_col(5.into()), LineCol { line: 0, col: 5.into() });
    assert_eq!(index.line_col(6.into()), LineCol { line: 1, col: 0.into() });
    assert_eq!(index.line_col(7.into()), LineCol { line: 1, col: 1.into() });
    assert_eq!(index.line_col(8.into()), LineCol { line: 1, col: 2.into() });
    assert_eq!(index.line_col(10.into()), LineCol { line: 1, col: 4.into() });
    assert_eq!(index.line_col(11.into()), LineCol { line: 1, col: 5.into() });
    assert_eq!(index.line_col(12.into()), LineCol { line: 1, col: 6.into() });

    let text = "\nhello\nworld";
    let index = LineIndex::new(text);
    assert_eq!(index.line_col(0.into()), LineCol { line: 0, col: 0.into() });
    assert_eq!(index.line_col(1.into()), LineCol { line: 1, col: 0.into() });
    assert_eq!(index.line_col(2.into()), LineCol { line: 1, col: 1.into() });
    assert_eq!(index.line_col(6.into()), LineCol { line: 1, col: 5.into() });
    assert_eq!(index.line_col(7.into()), LineCol { line: 2, col: 0.into() });
}