diff options
Diffstat (limited to 'crates/ra_syntax/src/ast/tokens.rs')
-rw-r--r-- | crates/ra_syntax/src/ast/tokens.rs | 69 |
1 files changed, 39 insertions, 30 deletions
diff --git a/crates/ra_syntax/src/ast/tokens.rs b/crates/ra_syntax/src/ast/tokens.rs index 3cd6d99c3..045f69133 100644 --- a/crates/ra_syntax/src/ast/tokens.rs +++ b/crates/ra_syntax/src/ast/tokens.rs | |||
@@ -1,6 +1,9 @@ | |||
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 | ||
3 | use std::convert::{TryFrom, TryInto}; | 3 | use std::{ |
4 | borrow::Cow, | ||
5 | convert::{TryFrom, TryInto}, | ||
6 | }; | ||
4 | 7 | ||
5 | use crate::{ | 8 | use crate::{ |
6 | ast::{AstToken, Comment, RawString, String, Whitespace}, | 9 | ast::{AstToken, Comment, RawString, String, Whitespace}, |
@@ -84,7 +87,7 @@ impl Whitespace { | |||
84 | } | 87 | } |
85 | 88 | ||
86 | pub struct QuoteOffsets { | 89 | pub struct QuoteOffsets { |
87 | pub quotes: [TextRange; 2], | 90 | pub quotes: (TextRange, TextRange), |
88 | pub contents: TextRange, | 91 | pub contents: TextRange, |
89 | } | 92 | } |
90 | 93 | ||
@@ -103,7 +106,7 @@ impl QuoteOffsets { | |||
103 | let end = TextSize::of(literal); | 106 | let end = TextSize::of(literal); |
104 | 107 | ||
105 | let res = QuoteOffsets { | 108 | let res = QuoteOffsets { |
106 | quotes: [TextRange::new(start, left_quote), TextRange::new(right_quote, end)], | 109 | quotes: (TextRange::new(start, left_quote), TextRange::new(right_quote, end)), |
107 | contents: TextRange::new(left_quote, right_quote), | 110 | contents: TextRange::new(left_quote, right_quote), |
108 | }; | 111 | }; |
109 | Some(res) | 112 | Some(res) |
@@ -116,17 +119,17 @@ pub trait HasQuotes: AstToken { | |||
116 | let offsets = QuoteOffsets::new(text)?; | 119 | let offsets = QuoteOffsets::new(text)?; |
117 | let o = self.syntax().text_range().start(); | 120 | let o = self.syntax().text_range().start(); |
118 | let offsets = QuoteOffsets { | 121 | let offsets = QuoteOffsets { |
119 | quotes: [offsets.quotes[0] + o, offsets.quotes[1] + o], | 122 | quotes: (offsets.quotes.0 + o, offsets.quotes.1 + o), |
120 | contents: offsets.contents + o, | 123 | contents: offsets.contents + o, |
121 | }; | 124 | }; |
122 | Some(offsets) | 125 | Some(offsets) |
123 | } | 126 | } |
124 | fn open_quote_text_range(&self) -> Option<TextRange> { | 127 | fn open_quote_text_range(&self) -> Option<TextRange> { |
125 | self.quote_offsets().map(|it| it.quotes[0]) | 128 | self.quote_offsets().map(|it| it.quotes.0) |
126 | } | 129 | } |
127 | 130 | ||
128 | fn close_quote_text_range(&self) -> Option<TextRange> { | 131 | fn close_quote_text_range(&self) -> Option<TextRange> { |
129 | self.quote_offsets().map(|it| it.quotes[1]) | 132 | self.quote_offsets().map(|it| it.quotes.1) |
130 | } | 133 | } |
131 | 134 | ||
132 | fn text_range_between_quotes(&self) -> Option<TextRange> { | 135 | fn text_range_between_quotes(&self) -> Option<TextRange> { |
@@ -138,11 +141,11 @@ impl HasQuotes for String {} | |||
138 | impl HasQuotes for RawString {} | 141 | impl HasQuotes for RawString {} |
139 | 142 | ||
140 | pub trait HasStringValue: HasQuotes { | 143 | pub trait HasStringValue: HasQuotes { |
141 | fn value(&self) -> Option<std::string::String>; | 144 | fn value(&self) -> Option<Cow<'_, str>>; |
142 | } | 145 | } |
143 | 146 | ||
144 | impl HasStringValue for String { | 147 | impl HasStringValue for String { |
145 | fn value(&self) -> Option<std::string::String> { | 148 | fn value(&self) -> Option<Cow<'_, str>> { |
146 | let text = self.text().as_str(); | 149 | let text = self.text().as_str(); |
147 | let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; | 150 | let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; |
148 | 151 | ||
@@ -156,15 +159,17 @@ impl HasStringValue for String { | |||
156 | if has_error { | 159 | if has_error { |
157 | return None; | 160 | return None; |
158 | } | 161 | } |
159 | Some(buf) | 162 | // FIXME: don't actually allocate for borrowed case |
163 | let res = if buf == text { Cow::Borrowed(text) } else { Cow::Owned(buf) }; | ||
164 | Some(res) | ||
160 | } | 165 | } |
161 | } | 166 | } |
162 | 167 | ||
163 | impl HasStringValue for RawString { | 168 | impl HasStringValue for RawString { |
164 | fn value(&self) -> Option<std::string::String> { | 169 | fn value(&self) -> Option<Cow<'_, str>> { |
165 | let text = self.text().as_str(); | 170 | let text = self.text().as_str(); |
166 | let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; | 171 | let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; |
167 | Some(text.to_string()) | 172 | Some(Cow::Borrowed(text)) |
168 | } | 173 | } |
169 | } | 174 | } |
170 | 175 | ||
@@ -335,16 +340,26 @@ pub trait HasFormatSpecifier: AstToken { | |||
335 | } | 340 | } |
336 | c if c == '_' || c.is_alphabetic() => { | 341 | c if c == '_' || c.is_alphabetic() => { |
337 | read_identifier(&mut chars, &mut callback); | 342 | read_identifier(&mut chars, &mut callback); |
338 | if chars.peek().and_then(|next| next.1.as_ref().ok()).copied() | 343 | // can be either width (indicated by dollar sign, or type in which case |
339 | != Some('$') | 344 | // the next sign has to be `}`) |
340 | { | 345 | let next = |
341 | continue; | 346 | chars.peek().and_then(|next| next.1.as_ref().ok()).copied(); |
342 | } | 347 | match next { |
343 | skip_char_and_emit( | 348 | Some('$') => skip_char_and_emit( |
344 | &mut chars, | 349 | &mut chars, |
345 | FormatSpecifier::DollarSign, | 350 | FormatSpecifier::DollarSign, |
346 | &mut callback, | 351 | &mut callback, |
347 | ); | 352 | ), |
353 | Some('}') => { | ||
354 | skip_char_and_emit( | ||
355 | &mut chars, | ||
356 | FormatSpecifier::Close, | ||
357 | &mut callback, | ||
358 | ); | ||
359 | continue; | ||
360 | } | ||
361 | _ => continue, | ||
362 | }; | ||
348 | } | 363 | } |
349 | _ => {} | 364 | _ => {} |
350 | } | 365 | } |
@@ -416,17 +431,11 @@ pub trait HasFormatSpecifier: AstToken { | |||
416 | } | 431 | } |
417 | } | 432 | } |
418 | 433 | ||
419 | let mut cloned = chars.clone().take(2); | 434 | if let Some((_, Ok('}'))) = chars.peek() { |
420 | let first = cloned.next().and_then(|next| next.1.as_ref().ok()).copied(); | 435 | skip_char_and_emit(&mut chars, FormatSpecifier::Close, &mut callback); |
421 | let second = cloned.next().and_then(|next| next.1.as_ref().ok()).copied(); | 436 | } else { |
422 | if first != Some('}') { | ||
423 | continue; | ||
424 | } | ||
425 | if second == Some('}') { | ||
426 | // Escaped format end specifier, `}}` | ||
427 | continue; | 437 | continue; |
428 | } | 438 | } |
429 | skip_char_and_emit(&mut chars, FormatSpecifier::Close, &mut callback); | ||
430 | } | 439 | } |
431 | _ => { | 440 | _ => { |
432 | while let Some((_, Ok(next_char))) = chars.peek() { | 441 | while let Some((_, Ok(next_char))) = chars.peek() { |