Skip to main content

just_lsp/
str_ext.rs

1use super::*;
2
3pub trait StrExt {
4  /// Returns a `Point` describing the tree-sitter point that would
5  /// be reached after inserting this UTF-8 text.
6  fn point_delta(&self) -> Point;
7}
8
9impl StrExt for str {
10  fn point_delta(&self) -> Point {
11    let (mut rows, mut column) = (0usize, 0usize);
12
13    let mut chars = self.chars().peekable();
14
15    while let Some(ch) = chars.next() {
16      match ch {
17        '\r' => {
18          if matches!(chars.peek().copied(), Some('\n')) {
19            chars.next();
20          }
21
22          rows += 1;
23          column = 0;
24        }
25        '\n' | '\u{000B}' | '\u{000C}' | '\u{0085}' | '\u{2028}'
26        | '\u{2029}' => {
27          rows += 1;
28          column = 0;
29        }
30        _ => {
31          column += ch.len_utf8();
32        }
33      }
34    }
35
36    Point::new(rows, column)
37  }
38}
39
40#[cfg(test)]
41mod tests {
42  use super::*;
43
44  #[test]
45  fn empty_string_produces_origin() {
46    assert_eq!("".point_delta(), Point::new(0, 0));
47  }
48
49  #[test]
50  fn ascii_text_advances_column_by_bytes() {
51    assert_eq!("abc".point_delta(), Point::new(0, 3));
52  }
53
54  #[test]
55  fn multibyte_chars_count_their_utf8_width() {
56    assert_eq!("😊é".point_delta(), Point::new(0, "😊é".len()));
57  }
58
59  #[test]
60  fn newline_moves_to_next_row_and_resets_column() {
61    assert_eq!("hi\n😊".point_delta(), Point::new(1, "😊".len()));
62  }
63
64  #[test]
65  fn crlf_sequences_count_as_single_newline() {
66    assert_eq!("\r\nabc".point_delta(), Point::new(1, "abc".len()));
67  }
68
69  #[test]
70  fn bare_carriage_return_counts_as_line_break() {
71    assert_eq!("foo\rbar".point_delta(), Point::new(1, "bar".len()));
72  }
73}