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.rs98
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;