diff options
Diffstat (limited to 'crates/syntax')
-rw-r--r-- | crates/syntax/src/ast/edit.rs | 205 | ||||
-rw-r--r-- | crates/syntax/src/ast/edit_in_place.rs | 130 | ||||
-rw-r--r-- | crates/syntax/src/ast/token_ext.rs | 81 | ||||
-rw-r--r-- | crates/syntax/src/parsing/text_tree_sink.rs | 4 | ||||
-rw-r--r-- | crates/syntax/test_data/parser/ok/0045_block_attrs.rast | 6 |
5 files changed, 177 insertions, 249 deletions
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs index 4b5f5c571..61952377f 100644 --- a/crates/syntax/src/ast/edit.rs +++ b/crates/syntax/src/ast/edit.rs | |||
@@ -10,12 +10,8 @@ use arrayvec::ArrayVec; | |||
10 | 10 | ||
11 | use crate::{ | 11 | use crate::{ |
12 | algo, | 12 | algo, |
13 | ast::{ | 13 | ast::{self, make, AstNode}, |
14 | self, | 14 | ted, AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxKind, |
15 | make::{self, tokens}, | ||
16 | AstNode, | ||
17 | }, | ||
18 | ted, AstToken, Direction, InsertPosition, NodeOrToken, SmolStr, SyntaxElement, SyntaxKind, | ||
19 | SyntaxKind::{ATTR, COMMENT, WHITESPACE}, | 15 | SyntaxKind::{ATTR, COMMENT, WHITESPACE}, |
20 | SyntaxNode, SyntaxToken, T, | 16 | SyntaxNode, SyntaxToken, T, |
21 | }; | 17 | }; |
@@ -29,114 +25,6 @@ impl ast::BinExpr { | |||
29 | } | 25 | } |
30 | } | 26 | } |
31 | 27 | ||
32 | fn make_multiline<N>(node: N) -> N | ||
33 | where | ||
34 | N: AstNode + Clone, | ||
35 | { | ||
36 | let l_curly = match node.syntax().children_with_tokens().find(|it| it.kind() == T!['{']) { | ||
37 | Some(it) => it, | ||
38 | None => return node, | ||
39 | }; | ||
40 | let sibling = match l_curly.next_sibling_or_token() { | ||
41 | Some(it) => it, | ||
42 | None => return node, | ||
43 | }; | ||
44 | let existing_ws = match sibling.as_token() { | ||
45 | None => None, | ||
46 | Some(tok) if tok.kind() != WHITESPACE => None, | ||
47 | Some(ws) => { | ||
48 | if ws.text().contains('\n') { | ||
49 | return node; | ||
50 | } | ||
51 | Some(ws.clone()) | ||
52 | } | ||
53 | }; | ||
54 | |||
55 | let indent = leading_indent(node.syntax()).unwrap_or_default(); | ||
56 | let ws = tokens::WsBuilder::new(&format!("\n{}", indent)); | ||
57 | let to_insert = iter::once(ws.ws().into()); | ||
58 | match existing_ws { | ||
59 | None => node.insert_children(InsertPosition::After(l_curly), to_insert), | ||
60 | Some(ws) => node.replace_children(single_node(ws), to_insert), | ||
61 | } | ||
62 | } | ||
63 | |||
64 | impl ast::RecordExprFieldList { | ||
65 | #[must_use] | ||
66 | pub fn append_field(&self, field: &ast::RecordExprField) -> ast::RecordExprFieldList { | ||
67 | self.insert_field(InsertPosition::Last, field) | ||
68 | } | ||
69 | |||
70 | #[must_use] | ||
71 | pub fn insert_field( | ||
72 | &self, | ||
73 | position: InsertPosition<&'_ ast::RecordExprField>, | ||
74 | field: &ast::RecordExprField, | ||
75 | ) -> ast::RecordExprFieldList { | ||
76 | let is_multiline = self.syntax().text().contains_char('\n'); | ||
77 | let ws; | ||
78 | let space = if is_multiline { | ||
79 | ws = tokens::WsBuilder::new(&format!( | ||
80 | "\n{} ", | ||
81 | leading_indent(self.syntax()).unwrap_or_default() | ||
82 | )); | ||
83 | ws.ws() | ||
84 | } else { | ||
85 | tokens::single_space() | ||
86 | }; | ||
87 | |||
88 | let mut to_insert: ArrayVec<SyntaxElement, 4> = ArrayVec::new(); | ||
89 | to_insert.push(space.into()); | ||
90 | to_insert.push(field.syntax().clone().into()); | ||
91 | to_insert.push(make::token(T![,]).into()); | ||
92 | |||
93 | macro_rules! after_l_curly { | ||
94 | () => {{ | ||
95 | let anchor = match self.l_curly_token() { | ||
96 | Some(it) => it.into(), | ||
97 | None => return self.clone(), | ||
98 | }; | ||
99 | InsertPosition::After(anchor) | ||
100 | }}; | ||
101 | } | ||
102 | |||
103 | macro_rules! after_field { | ||
104 | ($anchor:expr) => { | ||
105 | if let Some(comma) = $anchor | ||
106 | .syntax() | ||
107 | .siblings_with_tokens(Direction::Next) | ||
108 | .find(|it| it.kind() == T![,]) | ||
109 | { | ||
110 | InsertPosition::After(comma) | ||
111 | } else { | ||
112 | to_insert.insert(0, make::token(T![,]).into()); | ||
113 | InsertPosition::After($anchor.syntax().clone().into()) | ||
114 | } | ||
115 | }; | ||
116 | } | ||
117 | |||
118 | let position = match position { | ||
119 | InsertPosition::First => after_l_curly!(), | ||
120 | InsertPosition::Last => { | ||
121 | if !is_multiline { | ||
122 | // don't insert comma before curly | ||
123 | to_insert.pop(); | ||
124 | } | ||
125 | match self.fields().last() { | ||
126 | Some(it) => after_field!(it), | ||
127 | None => after_l_curly!(), | ||
128 | } | ||
129 | } | ||
130 | InsertPosition::Before(anchor) => { | ||
131 | InsertPosition::Before(anchor.syntax().clone().into()) | ||
132 | } | ||
133 | InsertPosition::After(anchor) => after_field!(anchor), | ||
134 | }; | ||
135 | |||
136 | self.insert_children(position, to_insert) | ||
137 | } | ||
138 | } | ||
139 | |||
140 | impl ast::Path { | 28 | impl ast::Path { |
141 | #[must_use] | 29 | #[must_use] |
142 | pub fn with_segment(&self, segment: ast::PathSegment) -> ast::Path { | 30 | pub fn with_segment(&self, segment: ast::PathSegment) -> ast::Path { |
@@ -214,79 +102,6 @@ impl ast::UseTree { | |||
214 | } | 102 | } |
215 | } | 103 | } |
216 | 104 | ||
217 | impl ast::MatchArmList { | ||
218 | #[must_use] | ||
219 | pub fn append_arms(&self, items: impl IntoIterator<Item = ast::MatchArm>) -> ast::MatchArmList { | ||
220 | let mut res = self.clone(); | ||
221 | res = res.strip_if_only_whitespace(); | ||
222 | if !res.syntax().text().contains_char('\n') { | ||
223 | res = make_multiline(res); | ||
224 | } | ||
225 | items.into_iter().for_each(|it| res = res.append_arm(it)); | ||
226 | res | ||
227 | } | ||
228 | |||
229 | fn strip_if_only_whitespace(&self) -> ast::MatchArmList { | ||
230 | let mut iter = self.syntax().children_with_tokens().skip_while(|it| it.kind() != T!['{']); | ||
231 | iter.next(); // Eat the curly | ||
232 | let mut inner = iter.take_while(|it| it.kind() != T!['}']); | ||
233 | if !inner.clone().all(|it| it.kind() == WHITESPACE) { | ||
234 | return self.clone(); | ||
235 | } | ||
236 | let start = match inner.next() { | ||
237 | Some(s) => s, | ||
238 | None => return self.clone(), | ||
239 | }; | ||
240 | let end = match inner.last() { | ||
241 | Some(s) => s, | ||
242 | None => start.clone(), | ||
243 | }; | ||
244 | self.replace_children(start..=end, &mut iter::empty()) | ||
245 | } | ||
246 | |||
247 | #[must_use] | ||
248 | pub fn remove_placeholder(&self) -> ast::MatchArmList { | ||
249 | let placeholder = | ||
250 | self.arms().find(|arm| matches!(arm.pat(), Some(ast::Pat::WildcardPat(_)))); | ||
251 | if let Some(placeholder) = placeholder { | ||
252 | self.remove_arm(&placeholder) | ||
253 | } else { | ||
254 | self.clone() | ||
255 | } | ||
256 | } | ||
257 | |||
258 | #[must_use] | ||
259 | fn remove_arm(&self, arm: &ast::MatchArm) -> ast::MatchArmList { | ||
260 | let start = arm.syntax().clone(); | ||
261 | let end = if let Some(comma) = start | ||
262 | .siblings_with_tokens(Direction::Next) | ||
263 | .skip(1) | ||
264 | .find(|it| !it.kind().is_trivia()) | ||
265 | .filter(|it| it.kind() == T![,]) | ||
266 | { | ||
267 | comma | ||
268 | } else { | ||
269 | start.clone().into() | ||
270 | }; | ||
271 | self.replace_children(start.into()..=end, None) | ||
272 | } | ||
273 | |||
274 | #[must_use] | ||
275 | pub fn append_arm(&self, item: ast::MatchArm) -> ast::MatchArmList { | ||
276 | let r_curly = match self.syntax().children_with_tokens().find(|it| it.kind() == T!['}']) { | ||
277 | Some(t) => t, | ||
278 | None => return self.clone(), | ||
279 | }; | ||
280 | let position = InsertPosition::Before(r_curly); | ||
281 | let arm_ws = tokens::WsBuilder::new(" "); | ||
282 | let match_indent = &leading_indent(self.syntax()).unwrap_or_default(); | ||
283 | let match_ws = tokens::WsBuilder::new(&format!("\n{}", match_indent)); | ||
284 | let to_insert: ArrayVec<SyntaxElement, 3> = | ||
285 | [arm_ws.ws().into(), item.syntax().clone().into(), match_ws.ws().into()].into(); | ||
286 | self.insert_children(position, to_insert) | ||
287 | } | ||
288 | } | ||
289 | |||
290 | #[must_use] | 105 | #[must_use] |
291 | pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N { | 106 | pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N { |
292 | N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap() | 107 | N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap() |
@@ -413,22 +228,6 @@ impl IndentLevel { | |||
413 | } | 228 | } |
414 | } | 229 | } |
415 | 230 | ||
416 | // FIXME: replace usages with IndentLevel above | ||
417 | fn leading_indent(node: &SyntaxNode) -> Option<SmolStr> { | ||
418 | for token in prev_tokens(node.first_token()?) { | ||
419 | if let Some(ws) = ast::Whitespace::cast(token.clone()) { | ||
420 | let ws_text = ws.text(); | ||
421 | if let Some(pos) = ws_text.rfind('\n') { | ||
422 | return Some(ws_text[pos + 1..].into()); | ||
423 | } | ||
424 | } | ||
425 | if token.text().contains('\n') { | ||
426 | break; | ||
427 | } | ||
428 | } | ||
429 | None | ||
430 | } | ||
431 | |||
432 | fn prev_tokens(token: SyntaxToken) -> impl Iterator<Item = SyntaxToken> { | 231 | fn prev_tokens(token: SyntaxToken) -> impl Iterator<Item = SyntaxToken> { |
433 | iter::successors(Some(token), |token| token.prev_token()) | 232 | iter::successors(Some(token), |token| token.prev_token()) |
434 | } | 233 | } |
diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs index ca777d057..14624c682 100644 --- a/crates/syntax/src/ast/edit_in_place.rs +++ b/crates/syntax/src/ast/edit_in_place.rs | |||
@@ -13,7 +13,7 @@ use crate::{ | |||
13 | make, GenericParamsOwner, | 13 | make, GenericParamsOwner, |
14 | }, | 14 | }, |
15 | ted::{self, Position}, | 15 | ted::{self, Position}, |
16 | AstNode, AstToken, Direction, | 16 | AstNode, AstToken, Direction, SyntaxNode, |
17 | }; | 17 | }; |
18 | 18 | ||
19 | use super::NameOwner; | 19 | use super::NameOwner; |
@@ -297,7 +297,7 @@ impl ast::AssocItemList { | |||
297 | ), | 297 | ), |
298 | None => match self.l_curly_token() { | 298 | None => match self.l_curly_token() { |
299 | Some(l_curly) => { | 299 | Some(l_curly) => { |
300 | self.normalize_ws_between_braces(); | 300 | normalize_ws_between_braces(self.syntax()); |
301 | (IndentLevel::from_token(&l_curly) + 1, Position::after(&l_curly), "\n") | 301 | (IndentLevel::from_token(&l_curly) + 1, Position::after(&l_curly), "\n") |
302 | } | 302 | } |
303 | None => (IndentLevel::single(), Position::last_child_of(self.syntax()), "\n"), | 303 | None => (IndentLevel::single(), Position::last_child_of(self.syntax()), "\n"), |
@@ -309,25 +309,6 @@ impl ast::AssocItemList { | |||
309 | ]; | 309 | ]; |
310 | ted::insert_all(position, elements); | 310 | ted::insert_all(position, elements); |
311 | } | 311 | } |
312 | |||
313 | fn normalize_ws_between_braces(&self) -> Option<()> { | ||
314 | let l = self.l_curly_token()?; | ||
315 | let r = self.r_curly_token()?; | ||
316 | let indent = IndentLevel::from_node(self.syntax()); | ||
317 | |||
318 | match l.next_sibling_or_token() { | ||
319 | Some(ws) if ws.kind() == SyntaxKind::WHITESPACE => { | ||
320 | if ws.next_sibling_or_token()?.into_token()? == r { | ||
321 | ted::replace(ws, make::tokens::whitespace(&format!("\n{}", indent))); | ||
322 | } | ||
323 | } | ||
324 | Some(ws) if ws.kind() == T!['}'] => { | ||
325 | ted::insert(Position::after(l), make::tokens::whitespace(&format!("\n{}", indent))); | ||
326 | } | ||
327 | _ => (), | ||
328 | } | ||
329 | Some(()) | ||
330 | } | ||
331 | } | 312 | } |
332 | 313 | ||
333 | impl ast::Fn { | 314 | impl ast::Fn { |
@@ -346,6 +327,113 @@ impl ast::Fn { | |||
346 | } | 327 | } |
347 | } | 328 | } |
348 | 329 | ||
330 | impl ast::MatchArm { | ||
331 | pub fn remove(&self) { | ||
332 | if let Some(sibling) = self.syntax().prev_sibling_or_token() { | ||
333 | if sibling.kind() == SyntaxKind::WHITESPACE { | ||
334 | ted::remove(sibling); | ||
335 | } | ||
336 | } | ||
337 | if let Some(sibling) = self.syntax().next_sibling_or_token() { | ||
338 | if sibling.kind() == T![,] { | ||
339 | ted::remove(sibling); | ||
340 | } | ||
341 | } | ||
342 | ted::remove(self.syntax()); | ||
343 | } | ||
344 | } | ||
345 | |||
346 | impl ast::MatchArmList { | ||
347 | pub fn add_arm(&self, arm: ast::MatchArm) { | ||
348 | normalize_ws_between_braces(self.syntax()); | ||
349 | let position = match self.arms().last() { | ||
350 | Some(last_arm) => { | ||
351 | let curly = last_arm | ||
352 | .syntax() | ||
353 | .siblings_with_tokens(Direction::Next) | ||
354 | .find(|it| it.kind() == T![,]); | ||
355 | Position::after(curly.unwrap_or_else(|| last_arm.syntax().clone().into())) | ||
356 | } | ||
357 | None => match self.l_curly_token() { | ||
358 | Some(it) => Position::after(it), | ||
359 | None => Position::last_child_of(self.syntax()), | ||
360 | }, | ||
361 | }; | ||
362 | let indent = IndentLevel::from_node(self.syntax()) + 1; | ||
363 | let elements = vec![ | ||
364 | make::tokens::whitespace(&format!("\n{}", indent)).into(), | ||
365 | arm.syntax().clone().into(), | ||
366 | ]; | ||
367 | ted::insert_all(position, elements); | ||
368 | } | ||
369 | } | ||
370 | |||
371 | impl ast::RecordExprFieldList { | ||
372 | pub fn add_field(&self, field: ast::RecordExprField) { | ||
373 | let is_multiline = self.syntax().text().contains_char('\n'); | ||
374 | let whitespace = if is_multiline { | ||
375 | let indent = IndentLevel::from_node(self.syntax()) + 1; | ||
376 | make::tokens::whitespace(&format!("\n{}", indent)) | ||
377 | } else { | ||
378 | make::tokens::single_space() | ||
379 | }; | ||
380 | |||
381 | let position = match self.fields().last() { | ||
382 | Some(last_field) => { | ||
383 | let comma = match last_field | ||
384 | .syntax() | ||
385 | .siblings_with_tokens(Direction::Next) | ||
386 | .filter_map(|it| it.into_token()) | ||
387 | .find(|it| it.kind() == T![,]) | ||
388 | { | ||
389 | Some(it) => it, | ||
390 | None => { | ||
391 | let comma = ast::make::token(T![,]); | ||
392 | ted::insert(Position::after(last_field.syntax()), &comma); | ||
393 | comma | ||
394 | } | ||
395 | }; | ||
396 | Position::after(comma) | ||
397 | } | ||
398 | None => match self.l_curly_token() { | ||
399 | Some(it) => Position::after(it), | ||
400 | None => Position::last_child_of(self.syntax()), | ||
401 | }, | ||
402 | }; | ||
403 | |||
404 | ted::insert_all(position, vec![whitespace.into(), field.syntax().clone().into()]); | ||
405 | if is_multiline { | ||
406 | ted::insert(Position::after(field.syntax()), ast::make::token(T![,])); | ||
407 | } | ||
408 | } | ||
409 | } | ||
410 | |||
411 | fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> { | ||
412 | let l = node | ||
413 | .children_with_tokens() | ||
414 | .filter_map(|it| it.into_token()) | ||
415 | .find(|it| it.kind() == T!['{'])?; | ||
416 | let r = node | ||
417 | .children_with_tokens() | ||
418 | .filter_map(|it| it.into_token()) | ||
419 | .find(|it| it.kind() == T!['}'])?; | ||
420 | |||
421 | let indent = IndentLevel::from_node(node); | ||
422 | |||
423 | match l.next_sibling_or_token() { | ||
424 | Some(ws) if ws.kind() == SyntaxKind::WHITESPACE => { | ||
425 | if ws.next_sibling_or_token()?.into_token()? == r { | ||
426 | ted::replace(ws, make::tokens::whitespace(&format!("\n{}", indent))); | ||
427 | } | ||
428 | } | ||
429 | Some(ws) if ws.kind() == T!['}'] => { | ||
430 | ted::insert(Position::after(l), make::tokens::whitespace(&format!("\n{}", indent))); | ||
431 | } | ||
432 | _ => (), | ||
433 | } | ||
434 | Some(()) | ||
435 | } | ||
436 | |||
349 | #[cfg(test)] | 437 | #[cfg(test)] |
350 | mod tests { | 438 | mod tests { |
351 | use std::fmt; | 439 | use std::fmt; |
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs index 29d25a58a..4b1e1ccee 100644 --- a/crates/syntax/src/ast/token_ext.rs +++ b/crates/syntax/src/ast/token_ext.rs | |||
@@ -143,6 +143,30 @@ impl QuoteOffsets { | |||
143 | } | 143 | } |
144 | } | 144 | } |
145 | 145 | ||
146 | pub trait IsString: AstToken { | ||
147 | fn quote_offsets(&self) -> Option<QuoteOffsets> { | ||
148 | let text = self.text(); | ||
149 | let offsets = QuoteOffsets::new(text)?; | ||
150 | let o = self.syntax().text_range().start(); | ||
151 | let offsets = QuoteOffsets { | ||
152 | quotes: (offsets.quotes.0 + o, offsets.quotes.1 + o), | ||
153 | contents: offsets.contents + o, | ||
154 | }; | ||
155 | Some(offsets) | ||
156 | } | ||
157 | fn text_range_between_quotes(&self) -> Option<TextRange> { | ||
158 | self.quote_offsets().map(|it| it.contents) | ||
159 | } | ||
160 | fn open_quote_text_range(&self) -> Option<TextRange> { | ||
161 | self.quote_offsets().map(|it| it.quotes.0) | ||
162 | } | ||
163 | fn close_quote_text_range(&self) -> Option<TextRange> { | ||
164 | self.quote_offsets().map(|it| it.quotes.1) | ||
165 | } | ||
166 | } | ||
167 | |||
168 | impl IsString for ast::String {} | ||
169 | |||
146 | impl ast::String { | 170 | impl ast::String { |
147 | pub fn is_raw(&self) -> bool { | 171 | pub fn is_raw(&self) -> bool { |
148 | self.text().starts_with('r') | 172 | self.text().starts_with('r') |
@@ -187,32 +211,49 @@ impl ast::String { | |||
187 | (false, false) => Some(Cow::Owned(buf)), | 211 | (false, false) => Some(Cow::Owned(buf)), |
188 | } | 212 | } |
189 | } | 213 | } |
190 | |||
191 | pub fn quote_offsets(&self) -> Option<QuoteOffsets> { | ||
192 | let text = self.text(); | ||
193 | let offsets = QuoteOffsets::new(text)?; | ||
194 | let o = self.syntax().text_range().start(); | ||
195 | let offsets = QuoteOffsets { | ||
196 | quotes: (offsets.quotes.0 + o, offsets.quotes.1 + o), | ||
197 | contents: offsets.contents + o, | ||
198 | }; | ||
199 | Some(offsets) | ||
200 | } | ||
201 | pub fn text_range_between_quotes(&self) -> Option<TextRange> { | ||
202 | self.quote_offsets().map(|it| it.contents) | ||
203 | } | ||
204 | pub fn open_quote_text_range(&self) -> Option<TextRange> { | ||
205 | self.quote_offsets().map(|it| it.quotes.0) | ||
206 | } | ||
207 | pub fn close_quote_text_range(&self) -> Option<TextRange> { | ||
208 | self.quote_offsets().map(|it| it.quotes.1) | ||
209 | } | ||
210 | } | 214 | } |
211 | 215 | ||
216 | impl IsString for ast::ByteString {} | ||
217 | |||
212 | impl ast::ByteString { | 218 | impl ast::ByteString { |
213 | pub fn is_raw(&self) -> bool { | 219 | pub fn is_raw(&self) -> bool { |
214 | self.text().starts_with("br") | 220 | self.text().starts_with("br") |
215 | } | 221 | } |
222 | |||
223 | pub fn value(&self) -> Option<Cow<'_, [u8]>> { | ||
224 | if self.is_raw() { | ||
225 | let text = self.text(); | ||
226 | let text = | ||
227 | &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; | ||
228 | return Some(Cow::Borrowed(text.as_bytes())); | ||
229 | } | ||
230 | |||
231 | let text = self.text(); | ||
232 | let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; | ||
233 | |||
234 | let mut buf: Vec<u8> = Vec::new(); | ||
235 | let mut text_iter = text.chars(); | ||
236 | let mut has_error = false; | ||
237 | unescape_literal(text, Mode::ByteStr, &mut |char_range, unescaped_char| match ( | ||
238 | unescaped_char, | ||
239 | buf.capacity() == 0, | ||
240 | ) { | ||
241 | (Ok(c), false) => buf.push(c as u8), | ||
242 | (Ok(c), true) if char_range.len() == 1 && Some(c) == text_iter.next() => (), | ||
243 | (Ok(c), true) => { | ||
244 | buf.reserve_exact(text.len()); | ||
245 | buf.extend_from_slice(&text[..char_range.start].as_bytes()); | ||
246 | buf.push(c as u8); | ||
247 | } | ||
248 | (Err(_), _) => has_error = true, | ||
249 | }); | ||
250 | |||
251 | match (has_error, buf.capacity() == 0) { | ||
252 | (true, _) => None, | ||
253 | (false, true) => Some(Cow::Borrowed(text.as_bytes())), | ||
254 | (false, false) => Some(Cow::Owned(buf)), | ||
255 | } | ||
256 | } | ||
216 | } | 257 | } |
217 | 258 | ||
218 | #[derive(Debug)] | 259 | #[derive(Debug)] |
diff --git a/crates/syntax/src/parsing/text_tree_sink.rs b/crates/syntax/src/parsing/text_tree_sink.rs index 1934204ea..d63ec080b 100644 --- a/crates/syntax/src/parsing/text_tree_sink.rs +++ b/crates/syntax/src/parsing/text_tree_sink.rs | |||
@@ -147,8 +147,8 @@ fn n_attached_trivias<'a>( | |||
147 | trivias: impl Iterator<Item = (SyntaxKind, &'a str)>, | 147 | trivias: impl Iterator<Item = (SyntaxKind, &'a str)>, |
148 | ) -> usize { | 148 | ) -> usize { |
149 | match kind { | 149 | match kind { |
150 | MACRO_CALL | MACRO_RULES | MACRO_DEF | CONST | TYPE_ALIAS | STRUCT | UNION | ENUM | 150 | CONST | ENUM | FN | IMPL | MACRO_CALL | MACRO_DEF | MACRO_RULES | MODULE | RECORD_FIELD |
151 | | VARIANT | FN | TRAIT | MODULE | RECORD_FIELD | STATIC | USE => { | 151 | | STATIC | STRUCT | TRAIT | TUPLE_FIELD | TYPE_ALIAS | UNION | USE | VARIANT => { |
152 | let mut res = 0; | 152 | let mut res = 0; |
153 | let mut trivias = trivias.enumerate().peekable(); | 153 | let mut trivias = trivias.enumerate().peekable(); |
154 | 154 | ||
diff --git a/crates/syntax/test_data/parser/ok/0045_block_attrs.rast b/crates/syntax/test_data/parser/ok/0045_block_attrs.rast index 50ab52d32..5e50b4e0b 100644 --- a/crates/syntax/test_data/parser/ok/0045_block_attrs.rast +++ b/crates/syntax/test_data/parser/ok/0045_block_attrs.rast | |||
@@ -127,9 +127,9 @@ [email protected] | |||
127 | [email protected] "\n" | 127 | [email protected] "\n" |
128 | [email protected] "}" | 128 | [email protected] "}" |
129 | [email protected] "\n\n" | 129 | [email protected] "\n\n" |
130 | COMMENT@541..601 "// https://github.com ..." | 130 | IMPL@541..763 |
131 | WHITESPACE@601..602 "\n" | 131 | COMMENT@541..601 "// https://github.com ..." |
132 | IMPL@602..763 | 132 | WHITESPACE@601..602 "\n" |
133 | [email protected] "impl" | 133 | [email protected] "impl" |
134 | [email protected] " " | 134 | [email protected] " " |
135 | [email protected] | 135 | [email protected] |