1use crate::syntax::{SyntaxElement, SyntaxKind, SyntaxNode};
2use rowan::TextRange;
3use rowan::TextSize;
4
5pub(crate) mod iter;
6pub(crate) mod shared;
7
8mod escape;
9pub mod syntax;
10
11pub use escape::check_escape;
12pub use escape::{escape, unescape};
13
14pub(crate) mod allowed_chars {
15 pub(crate) fn comment(s: &str) -> Result<(), Vec<usize>> {
16 let mut err_indices = Vec::new();
17
18 for (i, c) in s.chars().enumerate() {
19 if c != '\t' && c.is_control() {
20 err_indices.push(i);
21 }
22 }
23
24 if err_indices.is_empty() {
25 Ok(())
26 } else {
27 Err(err_indices)
28 }
29 }
30
31 pub(crate) fn string(s: &str) -> Result<(), Vec<usize>> {
32 let mut err_indices = Vec::new();
33
34 for (i, c) in s.chars().enumerate() {
35 if c != '\t'
36 && (('\u{0000}'..='\u{0008}').contains(&c)
37 || ('\u{000A}'..='\u{001F}').contains(&c)
38 || c == '\u{007F}')
39 {
40 err_indices.push(i);
41 }
42 }
43
44 if err_indices.is_empty() {
45 Ok(())
46 } else {
47 Err(err_indices)
48 }
49 }
50
51 pub(crate) fn multi_line_string(s: &str) -> Result<(), Vec<usize>> {
52 let mut err_indices = Vec::new();
53
54 for (i, c) in s.chars().enumerate() {
55 if c != '\t'
56 && c != '\n'
57 && c != '\r'
58 && (('\u{0000}'..='\u{0008}').contains(&c)
59 || ('\u{000A}'..='\u{001F}').contains(&c)
60 || c == '\u{007F}')
61 {
62 err_indices.push(i);
63 }
64 }
65
66 if err_indices.is_empty() {
67 Ok(())
68 } else {
69 Err(err_indices)
70 }
71 }
72
73 pub(crate) fn string_literal(s: &str) -> Result<(), Vec<usize>> {
74 let mut err_indices = Vec::new();
75
76 for (i, c) in s.chars().enumerate() {
77 if c != '\t' && c.is_control() {
78 err_indices.push(i);
79 }
80 }
81
82 if err_indices.is_empty() {
83 Ok(())
84 } else {
85 Err(err_indices)
86 }
87 }
88
89 pub(crate) fn multi_line_string_literal(s: &str) -> Result<(), Vec<usize>> {
90 let mut err_indices = Vec::new();
91
92 for (i, c) in s.chars().enumerate() {
93 if c != '\t' && c != '\n' && c != '\r' && c.is_control() {
94 err_indices.push(i);
95 }
96 }
97
98 if err_indices.is_empty() {
99 Ok(())
100 } else {
101 Err(err_indices)
102 }
103 }
104}
105
106pub trait StrExt {
107 fn strip_quotes(self) -> Self;
108}
109
110impl StrExt for &str {
111 fn strip_quotes(self) -> Self {
112 if self.starts_with('\"') || self.starts_with('\'') {
113 &self[1..self.len() - 1]
114 } else {
115 self
116 }
117 }
118}
119
120pub trait SyntaxExt {
122 fn find_node(&self, offset: TextSize, inclusive: bool) -> Option<SyntaxNode>;
124
125 fn find_node_deep(&self, offset: TextSize, inclusive: bool) -> Option<SyntaxNode> {
127 let mut node = self.find_node(offset, inclusive);
128 while let Some(n) = &node {
129 let new_node = n.find_node(offset, inclusive);
130 if new_node.is_some() {
131 node = new_node;
132 } else {
133 break;
134 }
135 }
136
137 node
138 }
139
140 fn find(&self, kind: SyntaxKind) -> Option<SyntaxElement>;
142}
143
144impl SyntaxExt for SyntaxNode {
145 fn find_node(&self, offset: TextSize, inclusive: bool) -> Option<SyntaxNode> {
146 for d in self.descendants().skip(1) {
147 let range = d.text_range();
148
149 if (inclusive && range.contains_inclusive(offset)) || range.contains(offset) {
150 return Some(d);
151 }
152 }
153
154 None
155 }
156
157 fn find(&self, kind: SyntaxKind) -> Option<SyntaxElement> {
158 self.descendants_with_tokens().find(|d| d.kind() == kind)
159 }
160}
161
162pub fn join_ranges<I: IntoIterator<Item = TextRange>>(ranges: I) -> TextRange {
163 ranges
164 .into_iter()
165 .fold(None, |ranges, range| match ranges {
166 Some(r) => Some(range.cover(r)),
167 None => Some(range),
168 })
169 .unwrap()
170}
171
172pub fn try_join_ranges<I: IntoIterator<Item = TextRange>>(ranges: I) -> Option<TextRange> {
173 ranges.into_iter().fold(None, |ranges, range| match ranges {
174 Some(r) => Some(range.cover(r)),
175 None => Some(range),
176 })
177}
178
179pub fn overlaps(range: TextRange, other: TextRange) -> bool {
180 range.contains_range(other)
181 || other.contains_range(range)
182 || range.contains(other.start())
183 || range.contains(other.end())
184 || other.contains(range.start())
185 || other.contains(range.end())
186}