A simple CPU rendered GUI IDE experience.
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
use std::ops::Range;

use implicit_fn::implicit_fn;
use lsp_types::Position;
use ropey::Rope;

pub trait RopeExt {
    fn position(&self, r: Range<usize>) -> Option<[(usize, usize); 2]>;
    /// number of lines
    fn l(&self) -> usize;

    // input: char, output: utf8
    fn x_bytes(&self, c: usize) -> Option<usize>;
    fn y(&self, c: usize) -> Option<usize>;
    fn x(&self, c: usize) -> Option<usize>;
    fn xy(&self, c: usize) -> Option<(usize, usize)>;

    fn beginning_of_line(&self, c: usize) -> Option<usize>;

    fn indentation_of(&self, n: usize) -> usize;

    /// or eof
    fn eol(&self, li: usize) -> usize;

    fn l_pos_to_char(&self, p: Position) -> Option<(usize, usize)>;
    fn l_position(&self, p: Position) -> Option<usize>;
    fn to_l_position(&self, l: usize) -> Option<lsp_types::Position>;
    fn l_range(&self, r: lsp_types::Range) -> Option<Range<usize>>;
    fn to_l_range(&self, r: Range<usize>) -> Option<lsp_types::Range>;
}
impl RopeExt for Rope {
    fn position(
        &self,
        Range { start, end }: Range<usize>,
    ) -> Option<[(usize, usize); 2]> {
        let y1 = self.try_char_to_line(start).ok()?;
        let y2 = self.try_char_to_line(end).ok()?;
        let x1 = start - self.try_line_to_char(y1).ok()?;
        let x2 = end - self.try_line_to_char(y2).ok()?;
        [(x1, y1), (x2, y2)].into()
    }
    /// number of lines
    fn l(&self) -> usize {
        self.len_lines()
    }

    // input: char, output: utf8
    fn x_bytes(&self, c: usize) -> Option<usize> {
        let y = self.try_char_to_line(c).ok()?;
        let x = self
            .try_char_to_byte(c)
            .ok()?
            .checked_sub(self.try_line_to_byte(y).ok()?)?;
        Some(x)
    }
    fn y(&self, c: usize) -> Option<usize> {
        self.try_char_to_line(c).ok()
    }

    fn xy(&self, c: usize) -> Option<(usize, usize)> {
        let y = self.try_char_to_line(c).ok()?;
        let x = c.checked_sub(self.try_line_to_char(y).ok()?)?;
        Some((x, y))
    }

    fn x(&self, c: usize) -> Option<usize> {
        self.xy(c).map(|x| x.0)
    }
    fn beginning_of_line(&self, c: usize) -> Option<usize> {
        self.y(c).and_then(|x| self.try_line_to_char(x).ok())
    }
    #[implicit_fn]
    fn indentation_of(&self, n: usize) -> usize {
        let Some(l) = self.get_line(n) else { return 0 };
        l.chars().filter(*_ != '\n').take_while(_.is_whitespace()).count()
    }

    /// or eof
    #[lower::apply(saturating)]
    fn eol(&self, li: usize) -> usize {
        self.try_line_to_char(li)
            .map(|l| {
                l + self
                    .get_line(li)
                    .map(|x| x.len_chars() - 1)
                    .unwrap_or_default()
            })
            .unwrap_or(usize::MAX)
            .min(self.len_chars())
    }
    fn l_pos_to_char(&self, p: Position) -> Option<(usize, usize)> {
        self.l_position(p).and_then(|x| self.xy(x))
    }

    fn l_position(&self, p: Position) -> Option<usize> {
        self.try_byte_to_char(
            self.try_line_to_byte(p.line as _).ok()?
                + (p.character as usize)
                    .min(self.get_line(p.line as _)?.len_bytes()),
        )
        .ok()
    }
    fn to_l_position(&self, l: usize) -> Option<lsp_types::Position> {
        Some(Position {
            line: self.y(l)? as _,
            character: self.x_bytes(l)? as _,
        })
    }
    fn l_range(&self, r: lsp_types::Range) -> Option<Range<usize>> {
        Some(self.l_position(r.start)?..self.l_position(r.end)?)
    }
    fn to_l_range(&self, r: Range<usize>) -> Option<lsp_types::Range> {
        Some(lsp_types::Range {
            start: self.to_l_position(r.start)?,
            end: self.to_l_position(r.end)?,
        })
    }
}