aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_syntax/src/ast
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_syntax/src/ast')
-rw-r--r--crates/ra_syntax/src/ast/make.rs11
-rw-r--r--crates/ra_syntax/src/ast/tokens.rs369
2 files changed, 371 insertions, 9 deletions
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs
index 0f4a50be4..ee0f5cc40 100644
--- a/crates/ra_syntax/src/ast/make.rs
+++ b/crates/ra_syntax/src/ast/make.rs
@@ -293,11 +293,20 @@ pub fn fn_def(
293 ast_from_text(&format!("fn {}{}{} {}", fn_name, type_params, params, body)) 293 ast_from_text(&format!("fn {}{}{} {}", fn_name, type_params, params, body))
294} 294}
295 295
296pub fn add_newlines(amount_of_newlines: usize, t: impl AstNode) -> ast::SourceFile { 296pub fn add_leading_newlines(amount_of_newlines: usize, t: impl AstNode) -> ast::SourceFile {
297 let newlines = "\n".repeat(amount_of_newlines); 297 let newlines = "\n".repeat(amount_of_newlines);
298 ast_from_text(&format!("{}{}", newlines, t.syntax())) 298 ast_from_text(&format!("{}{}", newlines, t.syntax()))
299} 299}
300 300
301pub fn add_trailing_newlines(amount_of_newlines: usize, t: impl AstNode) -> ast::SourceFile {
302 let newlines = "\n".repeat(amount_of_newlines);
303 ast_from_text(&format!("{}{}", t.syntax(), newlines))
304}
305
306pub fn add_pub_crate_modifier(fn_def: ast::FnDef) -> ast::FnDef {
307 ast_from_text(&format!("pub(crate) {}", fn_def))
308}
309
301fn ast_from_text<N: AstNode>(text: &str) -> N { 310fn ast_from_text<N: AstNode>(text: &str) -> N {
302 let parse = SourceFile::parse(text); 311 let parse = SourceFile::parse(text);
303 let node = parse.tree().syntax().descendants().find_map(N::cast).unwrap(); 312 let node = parse.tree().syntax().descendants().find_map(N::cast).unwrap();
diff --git a/crates/ra_syntax/src/ast/tokens.rs b/crates/ra_syntax/src/ast/tokens.rs
index e8320b57e..8e04b0bbd 100644
--- a/crates/ra_syntax/src/ast/tokens.rs
+++ b/crates/ra_syntax/src/ast/tokens.rs
@@ -1,8 +1,10 @@
1//! There are many AstNodes, but only a few tokens, so we hand-write them here. 1//! There are many AstNodes, but only a few tokens, so we hand-write them here.
2 2
3use std::convert::{TryFrom, TryInto};
4
3use crate::{ 5use crate::{
4 ast::{AstToken, Comment, RawString, String, Whitespace}, 6 ast::{AstToken, Comment, RawString, String, Whitespace},
5 TextRange, TextUnit, 7 TextRange, TextSize,
6}; 8};
7 9
8impl Comment { 10impl Comment {
@@ -94,14 +96,14 @@ impl QuoteOffsets {
94 return None; 96 return None;
95 } 97 }
96 98
97 let start = TextUnit::from(0); 99 let start = TextSize::from(0);
98 let left_quote = TextUnit::from_usize(left_quote) + TextUnit::of_char('"'); 100 let left_quote = TextSize::try_from(left_quote).unwrap() + TextSize::of('"');
99 let right_quote = TextUnit::from_usize(right_quote); 101 let right_quote = TextSize::try_from(right_quote).unwrap();
100 let end = TextUnit::of_str(literal); 102 let end = TextSize::of(literal);
101 103
102 let res = QuoteOffsets { 104 let res = QuoteOffsets {
103 quotes: [TextRange::from_to(start, left_quote), TextRange::from_to(right_quote, end)], 105 quotes: [TextRange::new(start, left_quote), TextRange::new(right_quote, end)],
104 contents: TextRange::from_to(left_quote, right_quote), 106 contents: TextRange::new(left_quote, right_quote),
105 }; 107 };
106 Some(res) 108 Some(res)
107 } 109 }
@@ -168,7 +170,358 @@ impl HasStringValue for RawString {
168impl RawString { 170impl RawString {
169 pub fn map_range_up(&self, range: TextRange) -> Option<TextRange> { 171 pub fn map_range_up(&self, range: TextRange) -> Option<TextRange> {
170 let contents_range = self.text_range_between_quotes()?; 172 let contents_range = self.text_range_between_quotes()?;
171 assert!(range.is_subrange(&TextRange::offset_len(0.into(), contents_range.len()))); 173 assert!(TextRange::up_to(contents_range.len()).contains_range(range));
172 Some(range + contents_range.start()) 174 Some(range + contents_range.start())
173 } 175 }
174} 176}
177
178#[derive(Debug)]
179pub enum FormatSpecifier {
180 Open,
181 Close,
182 Integer,
183 Identifier,
184 Colon,
185 Fill,
186 Align,
187 Sign,
188 NumberSign,
189 Zero,
190 DollarSign,
191 Dot,
192 Asterisk,
193 QuestionMark,
194}
195
196pub trait HasFormatSpecifier: AstToken {
197 fn char_ranges(
198 &self,
199 ) -> Option<Vec<(TextRange, Result<char, rustc_lexer::unescape::EscapeError>)>>;
200
201 fn lex_format_specifier<F>(&self, mut callback: F)
202 where
203 F: FnMut(TextRange, FormatSpecifier),
204 {
205 let char_ranges = if let Some(char_ranges) = self.char_ranges() {
206 char_ranges
207 } else {
208 return;
209 };
210 let mut chars = char_ranges.iter().peekable();
211
212 while let Some((range, first_char)) = chars.next() {
213 match first_char {
214 Ok('{') => {
215 // Format specifier, see syntax at https://doc.rust-lang.org/std/fmt/index.html#syntax
216 if let Some((_, Ok('{'))) = chars.peek() {
217 // Escaped format specifier, `{{`
218 chars.next();
219 continue;
220 }
221
222 callback(*range, FormatSpecifier::Open);
223
224 // check for integer/identifier
225 match chars
226 .peek()
227 .and_then(|next| next.1.as_ref().ok())
228 .copied()
229 .unwrap_or_default()
230 {
231 '0'..='9' => {
232 // integer
233 read_integer(&mut chars, &mut callback);
234 }
235 c if c == '_' || c.is_alphabetic() => {
236 // identifier
237 read_identifier(&mut chars, &mut callback);
238 }
239 _ => {}
240 }
241
242 if let Some((_, Ok(':'))) = chars.peek() {
243 skip_char_and_emit(&mut chars, FormatSpecifier::Colon, &mut callback);
244
245 // check for fill/align
246 let mut cloned = chars.clone().take(2);
247 let first = cloned
248 .next()
249 .and_then(|next| next.1.as_ref().ok())
250 .copied()
251 .unwrap_or_default();
252 let second = cloned
253 .next()
254 .and_then(|next| next.1.as_ref().ok())
255 .copied()
256 .unwrap_or_default();
257 match second {
258 '<' | '^' | '>' => {
259 // alignment specifier, first char specifies fillment
260 skip_char_and_emit(
261 &mut chars,
262 FormatSpecifier::Fill,
263 &mut callback,
264 );
265 skip_char_and_emit(
266 &mut chars,
267 FormatSpecifier::Align,
268 &mut callback,
269 );
270 }
271 _ => match first {
272 '<' | '^' | '>' => {
273 skip_char_and_emit(
274 &mut chars,
275 FormatSpecifier::Align,
276 &mut callback,
277 );
278 }
279 _ => {}
280 },
281 }
282
283 // check for sign
284 match chars
285 .peek()
286 .and_then(|next| next.1.as_ref().ok())
287 .copied()
288 .unwrap_or_default()
289 {
290 '+' | '-' => {
291 skip_char_and_emit(
292 &mut chars,
293 FormatSpecifier::Sign,
294 &mut callback,
295 );
296 }
297 _ => {}
298 }
299
300 // check for `#`
301 if let Some((_, Ok('#'))) = chars.peek() {
302 skip_char_and_emit(
303 &mut chars,
304 FormatSpecifier::NumberSign,
305 &mut callback,
306 );
307 }
308
309 // check for `0`
310 let mut cloned = chars.clone().take(2);
311 let first = cloned.next().and_then(|next| next.1.as_ref().ok()).copied();
312 let second = cloned.next().and_then(|next| next.1.as_ref().ok()).copied();
313
314 if first == Some('0') && second != Some('$') {
315 skip_char_and_emit(&mut chars, FormatSpecifier::Zero, &mut callback);
316 }
317
318 // width
319 match chars
320 .peek()
321 .and_then(|next| next.1.as_ref().ok())
322 .copied()
323 .unwrap_or_default()
324 {
325 '0'..='9' => {
326 read_integer(&mut chars, &mut callback);
327 if let Some((_, Ok('$'))) = chars.peek() {
328 skip_char_and_emit(
329 &mut chars,
330 FormatSpecifier::DollarSign,
331 &mut callback,
332 );
333 }
334 }
335 c if c == '_' || c.is_alphabetic() => {
336 read_identifier(&mut chars, &mut callback);
337 if chars.peek().and_then(|next| next.1.as_ref().ok()).copied()
338 != Some('$')
339 {
340 continue;
341 }
342 skip_char_and_emit(
343 &mut chars,
344 FormatSpecifier::DollarSign,
345 &mut callback,
346 );
347 }
348 _ => {}
349 }
350
351 // precision
352 if let Some((_, Ok('.'))) = chars.peek() {
353 skip_char_and_emit(&mut chars, FormatSpecifier::Dot, &mut callback);
354
355 match chars
356 .peek()
357 .and_then(|next| next.1.as_ref().ok())
358 .copied()
359 .unwrap_or_default()
360 {
361 '*' => {
362 skip_char_and_emit(
363 &mut chars,
364 FormatSpecifier::Asterisk,
365 &mut callback,
366 );
367 }
368 '0'..='9' => {
369 read_integer(&mut chars, &mut callback);
370 if let Some((_, Ok('$'))) = chars.peek() {
371 skip_char_and_emit(
372 &mut chars,
373 FormatSpecifier::DollarSign,
374 &mut callback,
375 );
376 }
377 }
378 c if c == '_' || c.is_alphabetic() => {
379 read_identifier(&mut chars, &mut callback);
380 if chars.peek().and_then(|next| next.1.as_ref().ok()).copied()
381 != Some('$')
382 {
383 continue;
384 }
385 skip_char_and_emit(
386 &mut chars,
387 FormatSpecifier::DollarSign,
388 &mut callback,
389 );
390 }
391 _ => {
392 continue;
393 }
394 }
395 }
396
397 // type
398 match chars
399 .peek()
400 .and_then(|next| next.1.as_ref().ok())
401 .copied()
402 .unwrap_or_default()
403 {
404 '?' => {
405 skip_char_and_emit(
406 &mut chars,
407 FormatSpecifier::QuestionMark,
408 &mut callback,
409 );
410 }
411 c if c == '_' || c.is_alphabetic() => {
412 read_identifier(&mut chars, &mut callback);
413 }
414 _ => {}
415 }
416 }
417
418 let mut cloned = chars.clone().take(2);
419 let first = cloned.next().and_then(|next| next.1.as_ref().ok()).copied();
420 let second = cloned.next().and_then(|next| next.1.as_ref().ok()).copied();
421 if first != Some('}') {
422 continue;
423 }
424 if second == Some('}') {
425 // Escaped format end specifier, `}}`
426 continue;
427 }
428 skip_char_and_emit(&mut chars, FormatSpecifier::Close, &mut callback);
429 }
430 _ => {
431 while let Some((_, Ok(next_char))) = chars.peek() {
432 match next_char {
433 '{' => break,
434 _ => {}
435 }
436 chars.next();
437 }
438 }
439 };
440 }
441
442 fn skip_char_and_emit<'a, I, F>(
443 chars: &mut std::iter::Peekable<I>,
444 emit: FormatSpecifier,
445 callback: &mut F,
446 ) where
447 I: Iterator<Item = &'a (TextRange, Result<char, rustc_lexer::unescape::EscapeError>)>,
448 F: FnMut(TextRange, FormatSpecifier),
449 {
450 let (range, _) = chars.next().unwrap();
451 callback(*range, emit);
452 }
453
454 fn read_integer<'a, I, F>(chars: &mut std::iter::Peekable<I>, callback: &mut F)
455 where
456 I: Iterator<Item = &'a (TextRange, Result<char, rustc_lexer::unescape::EscapeError>)>,
457 F: FnMut(TextRange, FormatSpecifier),
458 {
459 let (mut range, c) = chars.next().unwrap();
460 assert!(c.as_ref().unwrap().is_ascii_digit());
461 while let Some((r, Ok(next_char))) = chars.peek() {
462 if next_char.is_ascii_digit() {
463 chars.next();
464 range = range.cover(*r);
465 } else {
466 break;
467 }
468 }
469 callback(range, FormatSpecifier::Integer);
470 }
471
472 fn read_identifier<'a, I, F>(chars: &mut std::iter::Peekable<I>, callback: &mut F)
473 where
474 I: Iterator<Item = &'a (TextRange, Result<char, rustc_lexer::unescape::EscapeError>)>,
475 F: FnMut(TextRange, FormatSpecifier),
476 {
477 let (mut range, c) = chars.next().unwrap();
478 assert!(c.as_ref().unwrap().is_alphabetic() || *c.as_ref().unwrap() == '_');
479 while let Some((r, Ok(next_char))) = chars.peek() {
480 if *next_char == '_' || next_char.is_ascii_digit() || next_char.is_alphabetic() {
481 chars.next();
482 range = range.cover(*r);
483 } else {
484 break;
485 }
486 }
487 callback(range, FormatSpecifier::Identifier);
488 }
489 }
490}
491
492impl HasFormatSpecifier for String {
493 fn char_ranges(
494 &self,
495 ) -> Option<Vec<(TextRange, Result<char, rustc_lexer::unescape::EscapeError>)>> {
496 let text = self.text().as_str();
497 let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
498 let offset = self.text_range_between_quotes()?.start() - self.syntax().text_range().start();
499
500 let mut res = Vec::with_capacity(text.len());
501 rustc_lexer::unescape::unescape_str(text, &mut |range, unescaped_char| {
502 res.push((
503 TextRange::new(range.start.try_into().unwrap(), range.end.try_into().unwrap())
504 + offset,
505 unescaped_char,
506 ))
507 });
508
509 Some(res)
510 }
511}
512
513impl HasFormatSpecifier for RawString {
514 fn char_ranges(
515 &self,
516 ) -> Option<Vec<(TextRange, Result<char, rustc_lexer::unescape::EscapeError>)>> {
517 let text = self.text().as_str();
518 let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
519 let offset = self.text_range_between_quotes()?.start() - self.syntax().text_range().start();
520
521 let mut res = Vec::with_capacity(text.len());
522 for (idx, c) in text.char_indices() {
523 res.push((TextRange::at(idx.try_into().unwrap(), TextSize::of(c)) + offset, Ok(c)));
524 }
525 Some(res)
526 }
527}