diff options
author | Aleksey Kladov <[email protected]> | 2020-02-27 16:19:53 +0000 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2020-02-27 16:19:53 +0000 |
commit | e74484e1338b7d90038fc996208bb15e4e162f0f (patch) | |
tree | d08350bd15b6b950c9254215e7e04c2de03fcd0e /crates/ra_syntax/src | |
parent | fedab39011e1225b580f5afce8607c8eedad77e1 (diff) |
Refactor string literals
Diffstat (limited to 'crates/ra_syntax/src')
-rw-r--r-- | crates/ra_syntax/src/ast/tokens.rs | 134 |
1 files changed, 74 insertions, 60 deletions
diff --git a/crates/ra_syntax/src/ast/tokens.rs b/crates/ra_syntax/src/ast/tokens.rs index 693b35feb..1a51b8d3b 100644 --- a/crates/ra_syntax/src/ast/tokens.rs +++ b/crates/ra_syntax/src/ast/tokens.rs | |||
@@ -110,6 +110,64 @@ impl Whitespace { | |||
110 | } | 110 | } |
111 | } | 111 | } |
112 | 112 | ||
113 | pub struct QuoteOffsets { | ||
114 | pub quotes: [TextRange; 2], | ||
115 | pub contents: TextRange, | ||
116 | } | ||
117 | |||
118 | impl QuoteOffsets { | ||
119 | fn new(literal: &str) -> Option<QuoteOffsets> { | ||
120 | let left_quote = literal.find('"')?; | ||
121 | let right_quote = literal.rfind('"')?; | ||
122 | if left_quote == right_quote { | ||
123 | // `literal` only contains one quote | ||
124 | return None; | ||
125 | } | ||
126 | |||
127 | let start = TextUnit::from(0); | ||
128 | let left_quote = TextUnit::from_usize(left_quote) + TextUnit::of_char('"'); | ||
129 | let right_quote = TextUnit::from_usize(right_quote); | ||
130 | let end = TextUnit::of_str(literal); | ||
131 | |||
132 | let res = QuoteOffsets { | ||
133 | quotes: [TextRange::from_to(start, left_quote), TextRange::from_to(right_quote, end)], | ||
134 | contents: TextRange::from_to(left_quote, right_quote), | ||
135 | }; | ||
136 | Some(res) | ||
137 | } | ||
138 | } | ||
139 | |||
140 | pub trait HasQuotes: AstToken { | ||
141 | fn quote_offsets(&self) -> Option<QuoteOffsets> { | ||
142 | let text = self.text().as_str(); | ||
143 | let offsets = QuoteOffsets::new(text)?; | ||
144 | let o = self.syntax().text_range().start(); | ||
145 | let offsets = QuoteOffsets { | ||
146 | quotes: [offsets.quotes[0] + o, offsets.quotes[1] + o], | ||
147 | contents: offsets.contents + o, | ||
148 | }; | ||
149 | Some(offsets) | ||
150 | } | ||
151 | fn open_quote_text_range(&self) -> Option<TextRange> { | ||
152 | self.quote_offsets().map(|it| it.quotes[0]) | ||
153 | } | ||
154 | |||
155 | fn close_quote_text_range(&self) -> Option<TextRange> { | ||
156 | self.quote_offsets().map(|it| it.quotes[1]) | ||
157 | } | ||
158 | |||
159 | fn text_range_between_quotes(&self) -> Option<TextRange> { | ||
160 | self.quote_offsets().map(|it| it.contents) | ||
161 | } | ||
162 | } | ||
163 | |||
164 | impl HasQuotes for String {} | ||
165 | impl HasQuotes for RawString {} | ||
166 | |||
167 | pub trait HasStringValue: HasQuotes { | ||
168 | fn value(&self) -> Option<std::string::String>; | ||
169 | } | ||
170 | |||
113 | pub struct String(SyntaxToken); | 171 | pub struct String(SyntaxToken); |
114 | 172 | ||
115 | impl AstToken for String { | 173 | impl AstToken for String { |
@@ -124,21 +182,16 @@ impl AstToken for String { | |||
124 | } | 182 | } |
125 | } | 183 | } |
126 | 184 | ||
127 | impl String { | 185 | impl HasStringValue for String { |
128 | pub fn value(&self) -> Option<std::string::String> { | 186 | fn value(&self) -> Option<std::string::String> { |
129 | let text = self.text().as_str(); | 187 | let text = self.text().as_str(); |
130 | let usual_string_range = find_usual_string_range(text)?; | 188 | let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; |
131 | let start_of_inside = usual_string_range.start().to_usize() + 1; | ||
132 | let end_of_inside = usual_string_range.end().to_usize(); | ||
133 | let inside_str = &text[start_of_inside..end_of_inside]; | ||
134 | 189 | ||
135 | let mut buf = std::string::String::with_capacity(inside_str.len()); | 190 | let mut buf = std::string::String::with_capacity(text.len()); |
136 | let mut has_error = false; | 191 | let mut has_error = false; |
137 | rustc_lexer::unescape::unescape_str(inside_str, &mut |_, unescaped_char| { | 192 | rustc_lexer::unescape::unescape_str(text, &mut |_, unescaped_char| match unescaped_char { |
138 | match unescaped_char { | 193 | Ok(c) => buf.push(c), |
139 | Ok(c) => buf.push(c), | 194 | Err(_) => has_error = true, |
140 | Err(_) => has_error = true, | ||
141 | } | ||
142 | }); | 195 | }); |
143 | 196 | ||
144 | if has_error { | 197 | if has_error { |
@@ -162,57 +215,18 @@ impl AstToken for RawString { | |||
162 | } | 215 | } |
163 | } | 216 | } |
164 | 217 | ||
165 | impl RawString { | 218 | impl HasStringValue for RawString { |
166 | pub fn value(&self) -> Option<std::string::String> { | 219 | fn value(&self) -> Option<std::string::String> { |
167 | let text = self.text().as_str(); | ||
168 | let usual_string_range = find_usual_string_range(text)?; | ||
169 | let start_of_inside = usual_string_range.start().to_usize() + 1; | ||
170 | let end_of_inside = usual_string_range.end().to_usize(); | ||
171 | let inside_str = &text[start_of_inside..end_of_inside]; | ||
172 | Some(inside_str.to_string()) | ||
173 | } | ||
174 | |||
175 | pub fn open_quote_text_range(&self) -> Option<TextRange> { | ||
176 | let text = self.text().as_str(); | 220 | let text = self.text().as_str(); |
177 | let usual_string_range = find_usual_string_range(text)?; | 221 | let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; |
178 | 222 | Some(text.to_string()) | |
179 | let start = self.syntax().text_range().start(); | ||
180 | let len = usual_string_range.start() + TextUnit::of_char('"'); | ||
181 | Some(TextRange::offset_len(start, len)) | ||
182 | } | ||
183 | |||
184 | pub fn close_quote_text_range(&self) -> Option<TextRange> { | ||
185 | let text = self.text().as_str(); | ||
186 | let usual_string_range = find_usual_string_range(text)?; | ||
187 | |||
188 | let end = self.syntax().text_range().end(); | ||
189 | let len = TextUnit::of_str(text) - usual_string_range.end(); | ||
190 | Some(TextRange::from_to(end - len, end)) | ||
191 | } | 223 | } |
224 | } | ||
192 | 225 | ||
226 | impl RawString { | ||
193 | pub fn map_range_up(&self, range: TextRange) -> Option<TextRange> { | 227 | pub fn map_range_up(&self, range: TextRange) -> Option<TextRange> { |
194 | // FIXME: handle escapes here properly | 228 | let contents_range = self.text_range_between_quotes()?; |
195 | let text = self.text().as_str(); | 229 | assert!(range.is_subrange(&TextRange::offset_len(0.into(), contents_range.len()))); |
196 | let usual_string_range = find_usual_string_range(text)?; | 230 | Some(range + contents_range.start()) |
197 | Some( | ||
198 | range | ||
199 | + self.syntax().text_range().start() | ||
200 | + TextUnit::of_char('"') | ||
201 | + usual_string_range.start(), | ||
202 | ) | ||
203 | } | ||
204 | } | ||
205 | |||
206 | fn find_usual_string_range(s: &str) -> Option<TextRange> { | ||
207 | let left_quote = s.find('"')?; | ||
208 | let right_quote = s.rfind('"')?; | ||
209 | if left_quote == right_quote { | ||
210 | // `s` only contains one quote | ||
211 | None | ||
212 | } else { | ||
213 | Some(TextRange::from_to( | ||
214 | TextUnit::from(left_quote as u32), | ||
215 | TextUnit::from(right_quote as u32), | ||
216 | )) | ||
217 | } | 231 | } |
218 | } | 232 | } |