dprint_plugin_typescript/utils/
string_utils.rs1pub 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 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}