Skip to main content

dprint_plugin_typescript/utils/
string_utils.rs

1pub fn has_new_line_occurrences_in_leading_whitespace(text: &str, occurrences: i8) -> bool {
2  if occurrences == 0 {
3    return has_no_new_lines_in_leading_whitespace(text);
4  }
5
6  let mut found_occurrences = 0;
7  for c in text.chars() {
8    if !c.is_whitespace() {
9      return false;
10    }
11    if c == '\n' {
12      found_occurrences += 1;
13      if found_occurrences >= occurrences {
14        return true;
15      }
16    }
17  }
18
19  false
20}
21
22pub fn has_no_new_lines_in_leading_whitespace(text: &str) -> bool {
23  for c in text.chars() {
24    if !c.is_whitespace() {
25      return true;
26    }
27    if c == '\n' {
28      return false;
29    }
30  }
31
32  true
33}
34
35pub fn has_new_line_occurrences_in_trailing_whitespace(text: &str, occurrences: i8) -> bool {
36  if occurrences == 0 {
37    return has_no_new_lines_in_trailing_whitespace(text);
38  }
39
40  let mut found_occurrences = 0;
41  for c in text.chars().rev() {
42    if !c.is_whitespace() {
43      return false;
44    }
45    if c == '\n' {
46      found_occurrences += 1;
47      if found_occurrences >= occurrences {
48        return true;
49      }
50    }
51  }
52
53  false
54}
55
56pub fn has_no_new_lines_in_trailing_whitespace(text: &str) -> bool {
57  for c in text.chars().rev() {
58    if !c.is_whitespace() {
59      return true;
60    }
61    if c == '\n' {
62      return false;
63    }
64  }
65
66  true
67}
68
69pub fn is_not_empty_and_only_spaces(text: &str) -> bool {
70  if text.is_empty() {
71    return false;
72  }
73
74  for c in text.chars() {
75    if c != ' ' {
76      return false;
77    }
78  }
79
80  true
81}
82
83#[derive(Debug, PartialEq, Eq)]
84pub struct SplitLinesItem<'a> {
85  pub text: &'a str,
86  pub line_index: usize,
87  pub is_last: bool,
88}
89
90pub struct SplitLinesIterator<'a> {
91  text: &'a str,
92  inner: std::str::Split<'a, char>,
93  line_index: usize,
94  byte_index: usize,
95}
96
97impl<'a> Iterator for SplitLinesIterator<'a> {
98  type Item = SplitLinesItem<'a>;
99
100  fn next(&mut self) -> Option<Self::Item> {
101    let line_index = self.line_index;
102    let line = self.inner.next();
103    match line {
104      Some(line) => {
105        if self.line_index == 0 {
106          self.byte_index += line.len();
107        } else {
108          self.byte_index += 1 + line.len();
109        }
110        self.line_index += 1;
111        Some(SplitLinesItem {
112          is_last: self.byte_index == self.text.len(),
113          line_index,
114          text: line.strip_suffix('\r').unwrap_or(line),
115        })
116      }
117      None => None,
118    }
119  }
120}
121
122pub fn split_lines(text: &str) -> SplitLinesIterator<'_> {
123  SplitLinesIterator {
124    inner: text.split('\n'),
125    line_index: 0,
126    text,
127    byte_index: 0,
128  }
129}
130
131#[cfg(test)]
132mod test {
133  use super::*;
134
135  #[test]
136  fn split_lines_empty_last_line() {
137    let text = "a\r\nb\nc\r\n";
138    let lines = split_lines(text).collect::<Vec<_>>();
139    assert_eq!(
140      lines,
141      vec![
142        SplitLinesItem {
143          text: "a",
144          is_last: false,
145          line_index: 0,
146        },
147        SplitLinesItem {
148          text: "b",
149          is_last: false,
150          line_index: 1,
151        },
152        SplitLinesItem {
153          text: "c",
154          is_last: false,
155          line_index: 2
156        },
157        // includes last line
158        SplitLinesItem {
159          text: "",
160          is_last: true,
161          line_index: 3,
162        }
163      ]
164    );
165  }
166
167  #[test]
168  fn split_lines_non_empty_last_line() {
169    let text = "a \n   b\nc";
170    let lines = split_lines(text).collect::<Vec<_>>();
171    assert_eq!(
172      lines,
173      vec![
174        SplitLinesItem {
175          text: "a ",
176          line_index: 0,
177          is_last: false,
178        },
179        SplitLinesItem {
180          text: "   b",
181          line_index: 1,
182          is_last: false,
183        },
184        SplitLinesItem {
185          text: "c",
186          line_index: 2,
187          is_last: true,
188        }
189      ]
190    );
191  }
192}