Skip to main content

package_json_lsp/
document.rs

1use tower_lsp::lsp_types::{Position, Range, TextDocumentContentChangeEvent, Url};
2
3#[derive(Clone, Debug)]
4pub struct Document {
5    pub uri: Url,
6    pub version: i32,
7    text: String,
8}
9
10impl Document {
11    pub fn new(uri: Url, version: i32, text: String) -> Self {
12        Self { uri, version, text }
13    }
14
15    pub fn text(&self) -> &str {
16        &self.text
17    }
18
19    pub fn apply_changes(&mut self, changes: Vec<TextDocumentContentChangeEvent>) {
20        for change in changes {
21            if let Some(range) = change.range {
22                let start = self.offset_at(range.start);
23                let end = self.offset_at(range.end);
24                if start <= end && end <= self.text.len() {
25                    self.text.replace_range(start..end, &change.text);
26                }
27            } else {
28                self.text = change.text;
29            }
30        }
31    }
32
33    pub fn range_from_byte_range(&self, start: usize, end: usize) -> Range {
34        Range {
35            start: self.position_at(start),
36            end: self.position_at(end),
37        }
38    }
39
40    pub fn position_at(&self, offset: usize) -> Position {
41        let target = offset.min(self.text.len());
42        let mut line = 0_u32;
43        let mut character = 0_u32;
44
45        for (idx, ch) in self.text.char_indices() {
46            if idx >= target {
47                break;
48            }
49            if ch == '\n' {
50                line += 1;
51                character = 0;
52            } else {
53                character += ch.len_utf16() as u32;
54            }
55        }
56
57        Position { line, character }
58    }
59
60    pub fn offset_at(&self, position: Position) -> usize {
61        let mut line = 0_u32;
62        let mut character = 0_u32;
63
64        for (idx, ch) in self.text.char_indices() {
65            if line == position.line && character >= position.character {
66                return idx;
67            }
68            if ch == '\n' {
69                if line == position.line {
70                    return idx;
71                }
72                line += 1;
73                character = 0;
74            } else {
75                character += ch.len_utf16() as u32;
76            }
77        }
78
79        self.text.len()
80    }
81}
82
83pub fn position_in_range(position: Position, range: Range) -> bool {
84    if position.line < range.start.line || position.line > range.end.line {
85        return false;
86    }
87    if position.line == range.start.line && position.character < range.start.character {
88        return false;
89    }
90    if position.line == range.end.line && position.character > range.end.character {
91        return false;
92    }
93    true
94}