From c3148cb396bdcdb9802c56c95946093408bf8fc2 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 13 Jan 2022 19:12:39 +0100 Subject: [PATCH] Slightly cleanup HasFormatSpecifier::lex_format_specifier --- crates/syntax/src/ast/token_ext.rs | 391 ++++++++++++----------------- 1 file changed, 162 insertions(+), 229 deletions(-) diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs index cc7e5150b3..3b0a1c5ddc 100644 --- a/crates/syntax/src/ast/token_ext.rs +++ b/crates/syntax/src/ast/token_ext.rs @@ -288,287 +288,220 @@ pub trait HasFormatSpecifier: AstToken { Some(char_ranges) => char_ranges, None => return, }; - let mut chars = char_ranges.iter().peekable(); + let mut chars = char_ranges + .iter() + .filter_map(|(range, res)| Some((*range, *res.as_ref().ok()?))) + .peekable(); while let Some((range, first_char)) = chars.next() { - match first_char { - Ok('{') => { - // Format specifier, see syntax at https://doc.rust-lang.org/std/fmt/index.html#syntax - if let Some((_, Ok('{'))) = chars.peek() { - // Escaped format specifier, `{{` - chars.next(); - continue; + if let '{' = first_char { + // Format specifier, see syntax at https://doc.rust-lang.org/std/fmt/index.html#syntax + if let Some((_, '{')) = chars.peek() { + // Escaped format specifier, `{{` + chars.next(); + continue; + } + + callback(range, FormatSpecifier::Open); + + // check for integer/identifier + let (_, int_char) = chars.peek().copied().unwrap_or_default(); + match int_char { + // integer + '0'..='9' => read_integer(&mut chars, &mut callback), + // identifier + c if c == '_' || c.is_alphabetic() => { + read_identifier(&mut chars, &mut callback) } + _ => {} + } - callback(*range, FormatSpecifier::Open); + if let Some((_, ':')) = chars.peek() { + skip_char_and_emit(&mut chars, FormatSpecifier::Colon, &mut callback); - // check for integer/identifier - match chars - .peek() - .and_then(|next| next.1.as_ref().ok()) - .copied() - .unwrap_or_default() - { - '0'..='9' => { - // integer - read_integer(&mut chars, &mut callback); + // check for fill/align + let mut cloned = chars.clone().take(2); + let (_, first) = cloned.next().unwrap_or_default(); + let (_, second) = cloned.next().unwrap_or_default(); + match second { + '<' | '^' | '>' => { + // alignment specifier, first char specifies fillment + skip_char_and_emit(&mut chars, FormatSpecifier::Fill, &mut callback); + skip_char_and_emit(&mut chars, FormatSpecifier::Align, &mut callback); } - c if c == '_' || c.is_alphabetic() => { - // identifier - read_identifier(&mut chars, &mut callback); - } - _ => {} - } - - if let Some((_, Ok(':'))) = chars.peek() { - skip_char_and_emit(&mut chars, FormatSpecifier::Colon, &mut callback); - - // check for fill/align - let mut cloned = chars.clone().take(2); - let first = cloned - .next() - .and_then(|next| next.1.as_ref().ok()) - .copied() - .unwrap_or_default(); - let second = cloned - .next() - .and_then(|next| next.1.as_ref().ok()) - .copied() - .unwrap_or_default(); - match second { - '<' | '^' | '>' => { - // alignment specifier, first char specifies fillment - skip_char_and_emit( - &mut chars, - FormatSpecifier::Fill, - &mut callback, - ); + _ => { + if let '<' | '^' | '>' = first { skip_char_and_emit( &mut chars, FormatSpecifier::Align, &mut callback, ); } - _ => match first { - '<' | '^' | '>' => { - skip_char_and_emit( - &mut chars, - FormatSpecifier::Align, - &mut callback, - ); - } - _ => {} - }, } + } - // check for sign - match chars - .peek() - .and_then(|next| next.1.as_ref().ok()) - .copied() - .unwrap_or_default() - { - '+' | '-' => { + // check for sign + match chars.peek().copied().unwrap_or_default().1 { + '+' | '-' => { + skip_char_and_emit(&mut chars, FormatSpecifier::Sign, &mut callback); + } + _ => {} + } + + // check for `#` + if let Some((_, '#')) = chars.peek() { + skip_char_and_emit(&mut chars, FormatSpecifier::NumberSign, &mut callback); + } + + // check for `0` + let mut cloned = chars.clone().take(2); + let first = cloned.next().map(|next| next.1); + let second = cloned.next().map(|next| next.1); + + if first == Some('0') && second != Some('$') { + skip_char_and_emit(&mut chars, FormatSpecifier::Zero, &mut callback); + } + + // width + match chars.peek().copied().unwrap_or_default().1 { + '0'..='9' => { + read_integer(&mut chars, &mut callback); + if let Some((_, '$')) = chars.peek() { skip_char_and_emit( &mut chars, - FormatSpecifier::Sign, + FormatSpecifier::DollarSign, &mut callback, ); } - _ => {} } + c if c == '_' || c.is_alphabetic() => { + read_identifier(&mut chars, &mut callback); - // check for `#` - if let Some((_, Ok('#'))) = chars.peek() { - skip_char_and_emit( - &mut chars, - FormatSpecifier::NumberSign, - &mut callback, - ); - } - - // check for `0` - let mut cloned = chars.clone().take(2); - let first = cloned.next().and_then(|next| next.1.as_ref().ok()).copied(); - let second = cloned.next().and_then(|next| next.1.as_ref().ok()).copied(); - - if first == Some('0') && second != Some('$') { - skip_char_and_emit(&mut chars, FormatSpecifier::Zero, &mut callback); - } - - // width - match chars - .peek() - .and_then(|next| next.1.as_ref().ok()) - .copied() - .unwrap_or_default() - { - '0'..='9' => { - read_integer(&mut chars, &mut callback); - if let Some((_, Ok('$'))) = chars.peek() { - skip_char_and_emit( - &mut chars, - FormatSpecifier::DollarSign, - &mut callback, - ); - } - } - c if c == '_' || c.is_alphabetic() => { - read_identifier(&mut chars, &mut callback); - - if chars.peek().and_then(|next| next.1.as_ref().ok()).copied() - == Some('?') - { - skip_char_and_emit( - &mut chars, - FormatSpecifier::QuestionMark, - &mut callback, - ); - } - - // can be either width (indicated by dollar sign, or type in which case - // the next sign has to be `}`) - let next = - chars.peek().and_then(|next| next.1.as_ref().ok()).copied(); - - match next { - Some('$') => skip_char_and_emit( - &mut chars, - FormatSpecifier::DollarSign, - &mut callback, - ), - Some('}') => { - skip_char_and_emit( - &mut chars, - FormatSpecifier::Close, - &mut callback, - ); - continue; - } - _ => continue, - }; - } - _ => {} - } - - // precision - if let Some((_, Ok('.'))) = chars.peek() { - skip_char_and_emit(&mut chars, FormatSpecifier::Dot, &mut callback); - - match chars - .peek() - .and_then(|next| next.1.as_ref().ok()) - .copied() - .unwrap_or_default() - { - '*' => { - skip_char_and_emit( - &mut chars, - FormatSpecifier::Asterisk, - &mut callback, - ); - } - '0'..='9' => { - read_integer(&mut chars, &mut callback); - if let Some((_, Ok('$'))) = chars.peek() { - skip_char_and_emit( - &mut chars, - FormatSpecifier::DollarSign, - &mut callback, - ); - } - } - c if c == '_' || c.is_alphabetic() => { - read_identifier(&mut chars, &mut callback); - if chars.peek().and_then(|next| next.1.as_ref().ok()).copied() - != Some('$') - { - continue; - } - skip_char_and_emit( - &mut chars, - FormatSpecifier::DollarSign, - &mut callback, - ); - } - _ => { - continue; - } - } - } - - // type - match chars - .peek() - .and_then(|next| next.1.as_ref().ok()) - .copied() - .unwrap_or_default() - { - '?' => { + if chars.peek().map(|&(_, c)| c) == Some('?') { skip_char_and_emit( &mut chars, FormatSpecifier::QuestionMark, &mut callback, ); } - c if c == '_' || c.is_alphabetic() => { - read_identifier(&mut chars, &mut callback); - if chars.peek().and_then(|next| next.1.as_ref().ok()).copied() - == Some('?') - { + // can be either width (indicated by dollar sign, or type in which case + // the next sign has to be `}`) + let next = chars.peek().map(|&(_, c)| c); + + match next { + Some('$') => skip_char_and_emit( + &mut chars, + FormatSpecifier::DollarSign, + &mut callback, + ), + Some('}') => { skip_char_and_emit( &mut chars, - FormatSpecifier::QuestionMark, + FormatSpecifier::Close, + &mut callback, + ); + continue; + } + _ => continue, + }; + } + _ => {} + } + + // precision + if let Some((_, '.')) = chars.peek() { + skip_char_and_emit(&mut chars, FormatSpecifier::Dot, &mut callback); + + match chars.peek().copied().unwrap_or_default().1 { + '*' => { + skip_char_and_emit( + &mut chars, + FormatSpecifier::Asterisk, + &mut callback, + ); + } + '0'..='9' => { + read_integer(&mut chars, &mut callback); + if let Some((_, '$')) = chars.peek() { + skip_char_and_emit( + &mut chars, + FormatSpecifier::DollarSign, &mut callback, ); } } - _ => {} + c if c == '_' || c.is_alphabetic() => { + read_identifier(&mut chars, &mut callback); + if chars.peek().map(|&(_, c)| c) != Some('$') { + continue; + } + skip_char_and_emit( + &mut chars, + FormatSpecifier::DollarSign, + &mut callback, + ); + } + _ => { + continue; + } } } - match chars.peek() { - Some((_, Ok('}'))) => { - skip_char_and_emit(&mut chars, FormatSpecifier::Close, &mut callback); + // type + match chars.peek().copied().unwrap_or_default().1 { + '?' => { + skip_char_and_emit( + &mut chars, + FormatSpecifier::QuestionMark, + &mut callback, + ); } - Some((_, _)) | None => continue, + c if c == '_' || c.is_alphabetic() => { + read_identifier(&mut chars, &mut callback); + + if chars.peek().map(|&(_, c)| c) == Some('?') { + skip_char_and_emit( + &mut chars, + FormatSpecifier::QuestionMark, + &mut callback, + ); + } + } + _ => {} } } - _ => { - while let Some((_, Ok(next_char))) = chars.peek() { - if next_char == &'{' { - break; - } - chars.next(); - } + + if let Some((_, '}')) = chars.peek() { + skip_char_and_emit(&mut chars, FormatSpecifier::Close, &mut callback); } - }; + continue; + } } - fn skip_char_and_emit<'a, I, F>( + fn skip_char_and_emit( chars: &mut std::iter::Peekable, emit: FormatSpecifier, callback: &mut F, ) where - I: Iterator)>, + I: Iterator, F: FnMut(TextRange, FormatSpecifier), { let (range, _) = chars.next().unwrap(); - callback(*range, emit); + callback(range, emit); } - fn read_integer<'a, I, F>(chars: &mut std::iter::Peekable, callback: &mut F) + fn read_integer(chars: &mut std::iter::Peekable, callback: &mut F) where - I: Iterator)>, + I: Iterator, F: FnMut(TextRange, FormatSpecifier), { let (mut range, c) = chars.next().unwrap(); - assert!(c.as_ref().unwrap().is_ascii_digit()); - while let Some((r, Ok(next_char))) = chars.peek() { + assert!(c.is_ascii_digit()); + while let Some(&(r, next_char)) = chars.peek() { if next_char.is_ascii_digit() { chars.next(); - range = range.cover(*r); + range = range.cover(r); } else { break; } @@ -576,17 +509,17 @@ pub trait HasFormatSpecifier: AstToken { callback(range, FormatSpecifier::Integer); } - fn read_identifier<'a, I, F>(chars: &mut std::iter::Peekable, callback: &mut F) + fn read_identifier(chars: &mut std::iter::Peekable, callback: &mut F) where - I: Iterator)>, + I: Iterator, F: FnMut(TextRange, FormatSpecifier), { let (mut range, c) = chars.next().unwrap(); - assert!(c.as_ref().unwrap().is_alphabetic() || *c.as_ref().unwrap() == '_'); - while let Some((r, Ok(next_char))) = chars.peek() { - if *next_char == '_' || next_char.is_ascii_digit() || next_char.is_alphabetic() { + assert!(c.is_alphabetic() || c == '_'); + while let Some(&(r, next_char)) = chars.peek() { + if next_char == '_' || next_char.is_ascii_digit() || next_char.is_alphabetic() { chars.next(); - range = range.cover(*r); + range = range.cover(r); } else { break; }