1use std::{
20 ops::Range,
21 sync::atomic::{AtomicUsize, Ordering},
22};
23use texter::core::text::Text;
24use texter_impl::{change::WrapChange, updateable::WrapTree};
25use tree_sitter::{Point, Tree};
26
27use crate::errors::{DocumentError, PositionError, TexterError, TreeSitterError};
28
29pub(crate) mod texter_impl;
30
31#[derive(Debug, Clone)]
37pub struct Document {
38 pub texter: Text,
39 pub tree: Tree,
40}
41
42thread_local! {
43 pub static LAST_LINE: AtomicUsize = const { AtomicUsize::new(0) };
53}
54
55impl Document {
56 pub fn new(texter: Text, tree: Tree) -> Self {
57 Self { texter, tree }
58 }
59
60 pub fn as_str(&self) -> &str {
61 &self.texter.text
62 }
63
64 pub fn as_bytes(&self) -> &[u8] {
65 self.texter.text.as_bytes()
66 }
67
68 pub fn is_empty(&self) -> bool {
69 self.texter.text.is_empty()
70 }
71
72 pub fn update(
84 &mut self,
85 parser: &mut tree_sitter::Parser,
86 changes: &[lsp_types::TextDocumentContentChangeEvent],
87 ) -> Result<(), DocumentError> {
88 let mut new_tree = WrapTree::from(&mut self.tree);
89
90 for change in changes {
91 self.texter
92 .update(WrapChange::from(change).change, &mut new_tree)
93 .map_err(|e| DocumentError::from(TexterError::from(e)))?;
94 }
95
96 self.tree = parser
97 .parse(self.texter.text.as_bytes(), Some(&self.tree))
98 .ok_or_else(|| DocumentError::from(TreeSitterError::TreeSitterParser))?;
99
100 Ok(())
101 }
102
103 pub fn node_at_position(&self, position: lsp_types::Position) -> Option<tree_sitter::Node<'_>> {
105 let position = Point {
106 row: position.line as usize,
107 column: position.character as usize,
108 };
109
110 self.tree
111 .root_node()
112 .named_descendant_for_point_range(position, position)
113 }
114
115 pub fn node_range_at(&self, offset: usize) -> Option<lsp_types::Range> {
117 self.tree
118 .root_node()
119 .named_descendant_for_byte_range(offset, offset)
120 .map(|pos| lsp_types::Range {
121 start: lsp_types::Position {
122 line: pos.start_position().row as u32,
123 character: pos.start_position().column as u32,
124 },
125 end: lsp_types::Position {
126 line: pos.end_position().row as u32,
127 character: pos.end_position().column as u32,
128 },
129 })
130 }
131
132 pub fn position_at(&self, offset: usize) -> Result<lsp_types::Position, PositionError> {
134 let mut last_br_index = 0;
135 let last_line = LAST_LINE.with(|a| a.load(Ordering::SeqCst));
136
137 if self.texter.br_indexes.0.len() == 1 {
139 return if offset > self.texter.text.len() {
140 Err(PositionError::LineOutOfBound {
141 offset,
142 length: self.texter.text.len(),
143 })
144 } else {
145 Ok(lsp_types::Position {
146 line: 0,
147 character: offset as u32,
148 })
149 };
150 }
151
152 let start = match self.texter.br_indexes.0.get(last_line) {
154 Some(&br_index) if offset > br_index && last_line >= 1 => last_line, _ => 1, };
157
158 for (i, &br_index) in self.texter.br_indexes.0.iter().skip(start).enumerate() {
159 if offset <= br_index {
160 LAST_LINE.with(|a| a.store(i + (start - 1), Ordering::Release));
162
163 let col = offset.saturating_sub(last_br_index);
165
166 return Ok(lsp_types::Position {
167 line: (i + (start - 1)) as u32,
168 character: col as u32,
169 });
170 }
171
172 last_br_index = br_index + 1; }
174
175 if offset <= self.texter.text.len() {
176 let last_known_col = self.texter.br_indexes.0.iter().len();
177 let last_br = *self.texter.br_indexes.0.last().unwrap();
178 Ok(lsp_types::Position {
179 line: last_known_col.saturating_sub(1) as u32,
180 character: offset.saturating_sub(last_br) as u32,
181 })
182 } else {
183 Err(PositionError::WrongPosition { offset })
184 }
185 }
186
187 pub fn range_at(&self, range: Range<usize>) -> Result<lsp_types::Range, PositionError> {
189 let start = self
190 .position_at(range.start)
191 .map_err(|err| PositionError::WrongRange {
192 range: range.clone(),
193 position_error: Box::new(err),
194 })?;
195 let end = self
196 .position_at(range.end)
197 .map_err(|err| PositionError::WrongRange {
198 range: range.clone(),
199 position_error: Box::new(err),
200 })?;
201 Ok(lsp_types::Range { start, end })
202 }
203
204 pub fn offset_at(&self, position: lsp_types::Position) -> Option<usize> {
206 let line_index = self.texter.br_indexes.row_start(position.line as usize)?;
207 let line_str = self.texter.get_row(position.line as usize)?;
208 let col = position.character as usize;
209 if col > line_str.len() {
210 None
211 } else {
212 Some(line_index + col)
213 }
214 }
215}
216
217#[cfg(test)]
218mod test {
219 use super::*;
220 use lsp_types::Position;
221 use rstest::{fixture, rstest};
222 use tree_sitter::Parser;
223
224 #[fixture]
225 fn parser() -> Parser {
226 let mut p = Parser::new();
227 p.set_language(&tree_sitter_html::LANGUAGE.into()).unwrap();
228 p
229 }
230
231 fn get_last_line() -> usize {
232 use crate::document::LAST_LINE; use std::sync::atomic::Ordering;
234
235 LAST_LINE.with(|val| val.load(Ordering::Acquire))
236 }
237
238 #[rstest]
239 fn position_at(mut parser: Parser) {
240 let source = "<div>こんにちは\nGoodbye\r\nSee you!\n</div>";
241 let text = Text::new(source.into());
242 let document = Document::new(text, parser.parse(source, None).unwrap());
243
244 assert_eq!(&document.texter.br_indexes.0, &[0, 20, 29, 38]);
245
246 assert_eq!(
247 document.position_at(0).unwrap(),
248 Position {
249 line: 0,
250 character: 0
251 }
252 );
253
254 assert_eq!(
256 document.position_at(11).unwrap(),
257 Position {
258 line: 0,
259 character: 11
260 }
261 );
262
263 assert_eq!(
265 document.position_at(21).unwrap(),
266 Position {
267 line: 1,
268 character: 0
269 }
270 );
271
272 assert_eq!(
274 document.position_at(28).unwrap(),
275 Position {
276 line: 1,
277 character: 7
278 }
279 );
280
281 assert_eq!(
283 document.position_at(30).unwrap(),
284 Position {
285 line: 2,
286 character: 0
287 }
288 );
289
290 assert_eq!(
292 document.position_at(40).unwrap(),
293 Position {
294 line: 3,
295 character: 2
296 }
297 );
298 }
299
300 #[rstest]
301 fn position_at_single_line(mut parser: Parser) {
302 let source = "<div>AREALLYREALLYREALLYLONGTEXT<div>";
303 let text = Text::new(source.into());
304 let document = Document::new(text, parser.parse(source, None).unwrap());
305
306 assert_eq!(&document.texter.br_indexes.0, &[0]);
307
308 assert_eq!(
309 document.position_at(0).unwrap(),
310 Position {
311 line: 0,
312 character: 0
313 }
314 );
315
316 assert_eq!(
317 document.position_at(5).unwrap(),
318 Position {
319 line: 0,
320 character: 5
321 }
322 );
323
324 assert_eq!(
325 document.position_at(30).unwrap(),
326 Position {
327 line: 0,
328 character: 30
329 }
330 );
331 }
332
333 #[rstest]
334 fn range_at(mut parser: Parser) {
335 let source = "<div>こんにちは\nGoodbye\r\nSee you!\n</div>";
336 let text = Text::new(source.into());
337 let document = Document::new(text, parser.parse(source, None).unwrap());
338
339 assert_eq!(&document.texter.br_indexes.0, &[0, 20, 29, 38]);
340
341 assert_eq!(
343 document.range_at(0..11).unwrap(),
344 lsp_types::Range {
345 start: Position {
346 line: 0,
347 character: 0
348 },
349 end: Position {
350 line: 0,
351 character: 11
352 },
353 }
354 );
355
356 assert_eq!(
358 document.range_at(15..28).unwrap(),
359 lsp_types::Range {
360 start: Position {
361 line: 0,
362 character: 15
363 },
364 end: Position {
365 line: 1,
366 character: 7
367 },
368 }
369 );
370
371 assert_eq!(
373 document.range_at(21..30).unwrap(),
374 lsp_types::Range {
375 start: Position {
376 line: 1,
377 character: 0
378 },
379 end: Position {
380 line: 2,
381 character: 0
382 },
383 }
384 );
385
386 assert_eq!(
388 document.range_at(30..35).unwrap(),
389 lsp_types::Range {
390 start: Position {
391 line: 2,
392 character: 0
393 },
394 end: Position {
395 line: 2,
396 character: 5
397 },
398 }
399 );
400
401 assert_eq!(
403 document.range_at(35..50),
404 Err(PositionError::WrongRange {
405 range: 35..50,
406 position_error: Box::new(PositionError::WrongPosition { offset: 50 })
407 })
408 );
409 }
410
411 #[rstest]
412 fn range_at_single_line(mut parser: Parser) {
413 let source = "<div>AREALLYREALLYREALLYLONGTEXT<div>";
414 let text = Text::new(source.into());
415 let document = Document::new(text, parser.parse(source, None).unwrap());
416
417 assert_eq!(&document.texter.br_indexes.0, &[0]);
418
419 assert_eq!(&document.texter.br_indexes.0, &[0]);
421
422 assert_eq!(
424 document.range_at(0..5).unwrap(),
425 lsp_types::Range {
426 start: Position {
427 line: 0,
428 character: 0
429 },
430 end: Position {
431 line: 0,
432 character: 5
433 }
434 }
435 );
436
437 let length = source.len();
439 assert_eq!(
440 document.range_at(0..length).unwrap(),
441 lsp_types::Range {
442 start: Position {
443 line: 0,
444 character: 0
445 },
446 end: Position {
447 line: 0,
448 character: length as u32
449 }
450 }
451 );
452
453 assert_eq!(
455 document.range_at(0..(length + 5)),
456 Err(PositionError::WrongRange {
457 range: 0..(length + 5),
458 position_error: Box::new(PositionError::LineOutOfBound {
459 offset: 42,
460 length: 37
461 })
462 })
463 );
464 }
465
466 #[rstest]
467 fn offset_at(mut parser: Parser) {
468 let source = "Apples\nBashdjad\nashdkasdh\nasdsad";
469 let text = Text::new(source.into());
470 let document = Document::new(text, parser.parse(source, None).unwrap());
471
472 assert_eq!(&document.texter.br_indexes.0, &[0, 6, 15, 25]);
473
474 assert_eq!(
476 document.offset_at(Position {
477 line: 0,
478 character: 0
479 }),
480 Some(0)
481 );
482
483 assert_eq!(
485 document.offset_at(Position {
486 line: 0,
487 character: 5
488 }),
489 Some(5)
490 );
491
492 assert_eq!(
494 document.offset_at(Position {
495 line: 1,
496 character: 3
497 }),
498 Some(10)
499 );
500
501 assert_eq!(
503 document.offset_at(Position {
504 line: 3,
505 character: 5
506 }),
507 Some(31)
508 );
509
510 assert_eq!(
512 document.offset_at(Position {
513 line: 10,
514 character: 0
515 }),
516 None
517 );
518
519 assert_eq!(
521 document.offset_at(Position {
522 line: 1,
523 character: 100
524 }),
525 None
526 );
527 }
528
529 #[rstest]
530 fn line_tracking(mut parser: Parser) {
531 let source = "one\nline two\nline three\n";
532 let text = Text::new(source.into());
533 let document = Document::new(text, parser.parse(source, None).unwrap());
534
535 let pos1 = document.position_at(2).unwrap();
537 assert_eq!(pos1.line, 0);
538 assert_eq!(get_last_line(), 0);
539
540 let pos2 = document.position_at(6).unwrap();
542 assert_eq!(pos2.line, 1);
543 assert_eq!(get_last_line(), 1);
544
545 let pos3 = document.position_at(18).unwrap();
547 assert_eq!(pos3.line, 2);
548 assert_eq!(get_last_line(), 2);
549
550 let pos3 = document.position_at(0).unwrap();
553 assert_eq!(pos3.line, 0);
554 assert_eq!(get_last_line(), 0);
555 }
556}