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 | 98 |
1 files changed, 88 insertions, 10 deletions
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs index 4afdda78a0..e1a9f3ac03 100644 --- a/crates/syntax/src/ast/token_ext.rs +++ b/crates/syntax/src/ast/token_ext.rs @@ -151,10 +151,10 @@ impl QuoteOffsets { } pub trait IsString: AstToken { - const RAW_PREFIX: &'static str; - fn unescape(s: &str, callback: impl FnMut(Range<usize>, Result<char, EscapeError>)); + fn raw_prefix(&self) -> &'static str; + fn unescape(&self, s: &str, callback: impl FnMut(Range<usize>, Result<char, EscapeError>)); fn is_raw(&self) -> bool { - self.text().starts_with(Self::RAW_PREFIX) + self.text().starts_with(self.raw_prefix()) } fn quote_offsets(&self) -> Option<QuoteOffsets> { let text = self.text(); @@ -187,7 +187,7 @@ pub trait IsString: AstToken { let text = &self.text()[text_range_no_quotes - start]; let offset = text_range_no_quotes.start() - start; - Self::unescape(text, &mut |range: Range<usize>, unescaped_char| { + self.unescape(text, &mut |range: Range<usize>, unescaped_char| { if let Some((s, e)) = range.start.try_into().ok().zip(range.end.try_into().ok()) { cb(TextRange::new(s, e) + offset, unescaped_char); } @@ -201,11 +201,17 @@ pub trait IsString: AstToken { None } } + fn map_offset_down(&self, offset: TextSize) -> Option<TextSize> { + let contents_range = self.text_range_between_quotes()?; + offset.checked_sub(contents_range.start()) + } } impl IsString for ast::String { - const RAW_PREFIX: &'static str = "r"; - fn unescape(s: &str, cb: impl FnMut(Range<usize>, Result<char, EscapeError>)) { + fn raw_prefix(&self) -> &'static str { + "r" + } + fn unescape(&self, s: &str, cb: impl FnMut(Range<usize>, Result<char, EscapeError>)) { unescape_str(s, cb) } } @@ -246,8 +252,10 @@ impl ast::String { } impl IsString for ast::ByteString { - const RAW_PREFIX: &'static str = "br"; - fn unescape(s: &str, mut callback: impl FnMut(Range<usize>, Result<char, EscapeError>)) { + fn raw_prefix(&self) -> &'static str { + "br" + } + fn unescape(&self, s: &str, mut callback: impl FnMut(Range<usize>, Result<char, EscapeError>)) { unescape_byte_str(s, |range, res| callback(range, res.map(char::from))) } } @@ -288,10 +296,12 @@ impl ast::ByteString { } impl IsString for ast::CString { - const RAW_PREFIX: &'static str = "cr"; + fn raw_prefix(&self) -> &'static str { + "cr" + } // NOTE: This method should only be used for highlighting ranges. The unescaped // char/byte is not used. For simplicity, we return an arbitrary placeholder char. - fn unescape(s: &str, mut callback: impl FnMut(Range<usize>, Result<char, EscapeError>)) { + fn unescape(&self, s: &str, mut callback: impl FnMut(Range<usize>, Result<char, EscapeError>)) { unescape_c_str(s, |range, _res| callback(range, Ok('_'))) } } @@ -465,6 +475,74 @@ impl ast::Byte { } } +pub enum AnyString { + ByteString(ast::ByteString), + CString(ast::CString), + String(ast::String), +} + +impl AnyString { + pub fn value(&self) -> Result<Cow<'_, str>, EscapeError> { + fn from_utf8(s: Cow<'_, [u8]>) -> Result<Cow<'_, str>, EscapeError> { + match s { + Cow::Borrowed(s) => str::from_utf8(s) + .map_err(|_| EscapeError::NonAsciiCharInByte) + .map(Cow::Borrowed), + Cow::Owned(s) => String::from_utf8(s) + .map_err(|_| EscapeError::NonAsciiCharInByte) + .map(Cow::Owned), + } + } + + match self { + AnyString::String(s) => s.value(), + AnyString::ByteString(s) => s.value().and_then(from_utf8), + AnyString::CString(s) => s.value().and_then(from_utf8), + } + } +} + +impl ast::AstToken for AnyString { + fn can_cast(kind: crate::SyntaxKind) -> bool { + ast::String::can_cast(kind) + || ast::ByteString::can_cast(kind) + || ast::CString::can_cast(kind) + } + + fn cast(syntax: crate::SyntaxToken) -> Option<Self> { + ast::String::cast(syntax.clone()) + .map(Self::String) + .or_else(|| ast::ByteString::cast(syntax.clone()).map(Self::ByteString)) + .or_else(|| ast::CString::cast(syntax).map(Self::CString)) + } + + fn syntax(&self) -> &crate::SyntaxToken { + match self { + Self::ByteString(it) => it.syntax(), + Self::CString(it) => it.syntax(), + Self::String(it) => it.syntax(), + } + } +} + +impl IsString for AnyString { + fn raw_prefix(&self) -> &'static str { + match self { + AnyString::ByteString(s) => s.raw_prefix(), + AnyString::CString(s) => s.raw_prefix(), + AnyString::String(s) => s.raw_prefix(), + } + } + + fn unescape(&self, s: &str, callback: impl FnMut(Range<usize>, Result<char, EscapeError>)) { + match self { + AnyString::ByteString(it) => it.unescape(s, callback), + AnyString::CString(it) => it.unescape(s, callback), + AnyString::String(it) => it.unescape(s, callback), + } + } +} + #[cfg(test)] mod tests { use rustc_apfloat::ieee::Quad as f128; |