Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/syntax/src/ast/token_ext.rs')
| -rw-r--r-- | crates/syntax/src/ast/token_ext.rs | 71 |
1 files changed, 57 insertions, 14 deletions
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs index 2cd312e7f4..090eb89f47 100644 --- a/crates/syntax/src/ast/token_ext.rs +++ b/crates/syntax/src/ast/token_ext.rs @@ -145,6 +145,10 @@ impl QuoteOffsets { } pub trait IsString: AstToken { + const RAW_PREFIX: &'static str; + fn is_raw(&self) -> bool { + self.text().starts_with(Self::RAW_PREFIX) + } fn quote_offsets(&self) -> Option<QuoteOffsets> { let text = self.text(); let offsets = QuoteOffsets::new(text)?; @@ -183,20 +187,18 @@ pub trait IsString: AstToken { cb(text_range + offset, unescaped_char); }); } -} - -impl IsString for ast::String {} - -impl ast::String { - pub fn is_raw(&self) -> bool { - self.text().starts_with('r') - } - pub fn map_range_up(&self, range: TextRange) -> Option<TextRange> { + fn map_range_up(&self, range: TextRange) -> Option<TextRange> { let contents_range = self.text_range_between_quotes()?; assert!(TextRange::up_to(contents_range.len()).contains_range(range)); Some(range + contents_range.start()) } +} +impl IsString for ast::String { + const RAW_PREFIX: &'static str = "r"; +} + +impl ast::String { pub fn value(&self) -> Option<Cow<'_, str>> { if self.is_raw() { let text = self.text(); @@ -235,13 +237,11 @@ impl ast::String { } } -impl IsString for ast::ByteString {} +impl IsString for ast::ByteString { + const RAW_PREFIX: &'static str = "br"; +} impl ast::ByteString { - pub fn is_raw(&self) -> bool { - self.text().starts_with("br") - } - pub fn value(&self) -> Option<Cow<'_, [u8]>> { if self.is_raw() { let text = self.text(); @@ -280,6 +280,49 @@ impl ast::ByteString { } } +impl IsString for ast::CString { + const RAW_PREFIX: &'static str = "cr"; +} + +impl ast::CString { + pub fn value(&self) -> Option<Cow<'_, str>> { + if self.is_raw() { + let text = self.text(); + let text = + &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; + return Some(Cow::Borrowed(text)); + } + + let text = self.text(); + let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; + + let mut buf = String::new(); + let mut prev_end = 0; + let mut has_error = false; + unescape_literal(text, Mode::Str, &mut |char_range, unescaped_char| match ( + unescaped_char, + buf.capacity() == 0, + ) { + (Ok(c), false) => buf.push(c), + (Ok(_), true) if char_range.len() == 1 && char_range.start == prev_end => { + prev_end = char_range.end + } + (Ok(c), true) => { + buf.reserve_exact(text.len()); + buf.push_str(&text[..prev_end]); + buf.push(c); + } + (Err(_), _) => has_error = true, + }); + + match (has_error, buf.capacity() == 0) { + (true, _) => None, + (false, true) => Some(Cow::Borrowed(text)), + (false, false) => Some(Cow::Owned(buf)), + } + } +} + impl ast::IntNumber { pub fn radix(&self) -> Radix { match self.text().get(..2).unwrap_or_default() { |