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