aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_syntax/src/ast/tokens.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_syntax/src/ast/tokens.rs')
-rw-r--r--crates/ra_syntax/src/ast/tokens.rs134
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
113pub struct QuoteOffsets {
114 pub quotes: [TextRange; 2],
115 pub contents: TextRange,
116}
117
118impl 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
140pub 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
164impl HasQuotes for String {}
165impl HasQuotes for RawString {}
166
167pub trait HasStringValue: HasQuotes {
168 fn value(&self) -> Option<std::string::String>;
169}
170
113pub struct String(SyntaxToken); 171pub struct String(SyntaxToken);
114 172
115impl AstToken for String { 173impl AstToken for String {
@@ -124,21 +182,16 @@ impl AstToken for String {
124 } 182 }
125} 183}
126 184
127impl String { 185impl 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
165impl RawString { 218impl 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
226impl 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
206fn 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}