lsp_text_document/
document.rs

1use lsp_types::{Position, Range, TextDocumentContentChangeEvent, Url};
2#[derive(Clone)]
3pub struct FullTextDocument {
4    pub uri: Url,
5
6    /// The text document's language identifier.
7    pub language_id: String,
8
9    /// The version number of this document (it will strictly increase after each
10    /// change, including undo/redo).
11    pub version: i64,
12
13    /// The content of the opened text document.
14    pub text: String,
15
16    line_offset: Option<Vec<usize>>,
17}
18
19impl FullTextDocument {
20    pub fn new(uri: Url, language_id: String, version: i64, text: String) -> FullTextDocument {
21        // let item = lsp_types::TextDocumentItem::new(uri, language_id, version, text);
22        FullTextDocument {
23            uri,
24            language_id,
25            version,
26            text,
27            line_offset: None,
28        }
29    }
30
31    pub fn update(&mut self, changes: Vec<TextDocumentContentChangeEvent>, version: i64) {
32        for change in changes {
33            if Self::is_incremental(&change) {
34                // makes sure start is before end
35                let range = get_wellformed_range(change.range.unwrap());
36
37                let start_offset = self.offset_at(range.start);
38                let end_offset = self.offset_at(range.end);
39
40                let (start_byte, end_byte) = self.transform_offset_to_byte_offset(start_offset, end_offset);
41                self.text = self.text[0..start_byte].to_string()
42                    + &change.text
43                    + &self.text[end_byte..];
44                // self.text =
45                //     self.text.chars().take(start_offset).chain(change.text.chars()).chain(self.text.chars().skip(end_offset)).collect::<String>();
46                let start_line = range.start.line as usize;
47                let end_line = range.end.line as usize;
48                let line_offsets = self.get_line_offsets();
49
50                let mut add_line_offsets =
51                    compute_line_offsets(&change.text, false, Some(start_offset));
52
53                let add_line_offsets_len = add_line_offsets.len();
54                // if line_offsets.len() <= end_line as usize {
55                //     line_offsets.extend(vec![0; end_line as usize + 1 - line_offsets.len()]);
56                // }
57
58                if end_line - start_line == add_line_offsets.len() {
59                    for (i, offset) in add_line_offsets.into_iter().enumerate() {
60                        line_offsets[i + start_line + 1] = offset;
61                    }
62                } else {
63                    *line_offsets = {
64                        let mut res =
65                            line_offsets[0..=start_line.min(line_offsets.len() - 1)].to_vec();
66                        res.append(&mut add_line_offsets);
67                        res.extend_from_slice(
68                            &line_offsets[end_line.min(line_offsets.len() - 1) + 1..],
69                        );
70                        res
71                    };
72                }
73                let diff: i32 = change.text.len() as i32 - (end_offset - start_offset) as i32;
74                if diff != 0 {
75                    for i in start_line + 1 + add_line_offsets_len..line_offsets.len() {
76                        line_offsets[i] = (line_offsets[i] as i32 + diff) as usize;
77                    }
78                }
79            } else if Self::is_full(&change) {
80                self.text = change.text;
81                self.line_offset = None;
82            }
83            self.version = version;
84        }
85    }
86
87    pub fn transform_offset_to_byte_offset(&self, start_offset: usize, end_offset: usize) -> (usize, usize) {
88        let start_byte = self.text
89            .chars()
90            .take(start_offset)
91            .fold(0, |acc, cur| acc + cur.len_utf8());
92        let end_byte = (&self.text[start_offset..end_offset])
93            .chars()
94            .take(end_offset)
95            .fold(0, |acc, cur| acc + cur.len_utf8())
96            + start_byte;
97        (start_byte, end_byte)
98    }
99    pub fn position_at(&mut self, mut offset: u32) -> Position {
100        offset = offset.min(self.text.len() as u32).max(0);
101
102        let line_offsets = self.get_line_offsets();
103        // let low = 0, high = lineOffsets.length;
104        let mut low = 0usize;
105        let mut high = line_offsets.len();
106        if high == 0 {
107            return Position {
108                line: 0,
109                character: offset,
110            };
111        }
112        while low < high {
113            let mid = low + (high - low) / 2;
114            if line_offsets[mid] as u32 > offset {
115                high = mid;
116            } else {
117                low = mid + 1;
118            }
119        }
120        let line = low as u32 - 1;
121        return Position {
122            line,
123            character: offset - line_offsets[line as usize] as u32,
124        };
125        // while (low < high) {
126        // 	let mid = Math.floor((low + high) / 2);
127        // 	if (lineOffsets[mid] > offset) {
128        // 		high = mid;
129        // 	} else {
130        // 		low = mid + 1;
131        // 	}
132        // }
133        // // low is the least x for which the line offset is larger than the current offset
134        // // or array.length if no line offset is larger than the current offset
135        // let line = low - 1;
136        // return { line, character: offset - lineOffsets[line] };
137    }
138
139    pub fn line_count(&mut self) -> usize {
140        self.get_line_offsets().len()
141    }
142    pub fn is_incremental(event: &TextDocumentContentChangeEvent) -> bool {
143        event.range.is_some()
144    }
145
146    pub fn is_full(event: &TextDocumentContentChangeEvent) -> bool {
147        !event.range_length.is_some() && !event.range.is_some()
148    }
149
150    pub fn get_line_offsets(&mut self) -> &mut Vec<usize> {
151        if self.line_offset.is_none() {
152            self.line_offset = Some(compute_line_offsets(&self.text, true, None));
153        }
154        self.line_offset.as_mut().unwrap()
155    }
156    pub fn offset_at(&mut self, position: Position) -> usize {
157        let line_offsets = self.get_line_offsets();
158        if position.line >= line_offsets.len() as u32 {
159            return self.text.len();
160        }
161        let line_offset = line_offsets[position.line as usize];
162        let next_line_offset = if position.line + 1 < line_offsets.len() as u32 {
163            line_offsets[position.line as usize + 1]
164        } else {
165            self.text.len()
166        };
167        (line_offset + position.character as usize)
168            .min(next_line_offset)
169            .max(line_offset)
170        // return Math.max(
171        //     Math.min(line_offset + position.character, next_line_offset),
172        //     line_offset,
173        // );
174    }
175}
176
177pub fn compute_line_offsets(
178    text: &String,
179    is_at_line_start: bool,
180    text_offset: Option<usize>,
181) -> Vec<usize> {
182    let text_offset = if let Some(offset) = text_offset {
183        offset
184    } else {
185        0
186    };
187    let mut result = if is_at_line_start {
188        vec![text_offset]
189    } else {
190        vec![]
191    };
192    let char_array: Vec<char> = text.chars().collect();
193    let mut i = 0;
194    while i < char_array.len() {
195        let &ch = unsafe { char_array.get_unchecked(i) };
196        if ch == '\r' || ch == '\n' {
197            if ch == '\r'
198                && i + 1 < char_array.len()
199                && unsafe { char_array.get_unchecked(i + 1) == &'\n' }
200            {
201                i += 1;
202            }
203            result.push(text_offset + i + 1);
204        }
205        i += 1;
206    }
207    result
208}
209
210fn get_wellformed_range(range: Range) -> Range {
211    let start = range.start;
212    let end = range.end;
213    if start.line > end.line || (start.line == end.line && start.character > end.character) {
214        Range::new(end, start)
215    } else {
216        range
217    }
218}