diff options
Diffstat (limited to 'crates/ra_syntax')
-rw-r--r-- | crates/ra_syntax/src/algo.rs | 52 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/edit.rs | 11 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/make.rs | 11 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/tokens.rs | 110 | ||||
-rw-r--r-- | crates/ra_syntax/test_data/parser/inline/ok/0130_let_stmt.rs | 1 | ||||
-rw-r--r-- | crates/ra_syntax/test_data/parser/inline/ok/0130_let_stmt.txt | 41 |
6 files changed, 171 insertions, 55 deletions
diff --git a/crates/ra_syntax/src/algo.rs b/crates/ra_syntax/src/algo.rs index 21fca99a6..b87c7084e 100644 --- a/crates/ra_syntax/src/algo.rs +++ b/crates/ra_syntax/src/algo.rs | |||
@@ -4,10 +4,11 @@ use std::ops::RangeInclusive; | |||
4 | 4 | ||
5 | use itertools::Itertools; | 5 | use itertools::Itertools; |
6 | use ra_text_edit::TextEditBuilder; | 6 | use ra_text_edit::TextEditBuilder; |
7 | use rustc_hash::FxHashMap; | 7 | use rustc_hash::{FxHashMap, FxHashSet}; |
8 | 8 | ||
9 | use crate::{ | 9 | use crate::{ |
10 | AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxNodePtr, TextRange, TextUnit, | 10 | AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxNodePtr, SyntaxToken, |
11 | TextRange, TextUnit, | ||
11 | }; | 12 | }; |
12 | 13 | ||
13 | /// Returns ancestors of the node at the offset, sorted by length. This should | 14 | /// Returns ancestors of the node at the offset, sorted by length. This should |
@@ -37,6 +38,17 @@ pub fn find_node_at_offset<N: AstNode>(syntax: &SyntaxNode, offset: TextUnit) -> | |||
37 | ancestors_at_offset(syntax, offset).find_map(N::cast) | 38 | ancestors_at_offset(syntax, offset).find_map(N::cast) |
38 | } | 39 | } |
39 | 40 | ||
41 | /// Skip to next non `trivia` token | ||
42 | pub fn skip_trivia_token(mut token: SyntaxToken, direction: Direction) -> Option<SyntaxToken> { | ||
43 | while token.kind().is_trivia() { | ||
44 | token = match direction { | ||
45 | Direction::Next => token.next_token()?, | ||
46 | Direction::Prev => token.prev_token()?, | ||
47 | } | ||
48 | } | ||
49 | Some(token) | ||
50 | } | ||
51 | |||
40 | /// Finds the first sibling in the given direction which is not `trivia` | 52 | /// Finds the first sibling in the given direction which is not `trivia` |
41 | pub fn non_trivia_sibling(element: SyntaxElement, direction: Direction) -> Option<SyntaxElement> { | 53 | pub fn non_trivia_sibling(element: SyntaxElement, direction: Direction) -> Option<SyntaxElement> { |
42 | return match element { | 54 | return match element { |
@@ -56,6 +68,11 @@ pub fn find_covering_element(root: &SyntaxNode, range: TextRange) -> SyntaxEleme | |||
56 | root.covering_element(range) | 68 | root.covering_element(range) |
57 | } | 69 | } |
58 | 70 | ||
71 | pub fn least_common_ancestor(u: &SyntaxNode, v: &SyntaxNode) -> Option<SyntaxNode> { | ||
72 | let u_ancestors = u.ancestors().collect::<FxHashSet<SyntaxNode>>(); | ||
73 | v.ancestors().find(|it| u_ancestors.contains(it)) | ||
74 | } | ||
75 | |||
59 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | 76 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] |
60 | pub enum InsertPosition<T> { | 77 | pub enum InsertPosition<T> { |
61 | First, | 78 | First, |
@@ -127,6 +144,15 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff { | |||
127 | pub fn insert_children( | 144 | pub fn insert_children( |
128 | parent: &SyntaxNode, | 145 | parent: &SyntaxNode, |
129 | position: InsertPosition<SyntaxElement>, | 146 | position: InsertPosition<SyntaxElement>, |
147 | to_insert: impl IntoIterator<Item = SyntaxElement>, | ||
148 | ) -> SyntaxNode { | ||
149 | let mut to_insert = to_insert.into_iter(); | ||
150 | _insert_children(parent, position, &mut to_insert) | ||
151 | } | ||
152 | |||
153 | fn _insert_children( | ||
154 | parent: &SyntaxNode, | ||
155 | position: InsertPosition<SyntaxElement>, | ||
130 | to_insert: &mut dyn Iterator<Item = SyntaxElement>, | 156 | to_insert: &mut dyn Iterator<Item = SyntaxElement>, |
131 | ) -> SyntaxNode { | 157 | ) -> SyntaxNode { |
132 | let mut delta = TextUnit::default(); | 158 | let mut delta = TextUnit::default(); |
@@ -161,6 +187,15 @@ pub fn insert_children( | |||
161 | pub fn replace_children( | 187 | pub fn replace_children( |
162 | parent: &SyntaxNode, | 188 | parent: &SyntaxNode, |
163 | to_delete: RangeInclusive<SyntaxElement>, | 189 | to_delete: RangeInclusive<SyntaxElement>, |
190 | to_insert: impl IntoIterator<Item = SyntaxElement>, | ||
191 | ) -> SyntaxNode { | ||
192 | let mut to_insert = to_insert.into_iter(); | ||
193 | _replace_children(parent, to_delete, &mut to_insert) | ||
194 | } | ||
195 | |||
196 | fn _replace_children( | ||
197 | parent: &SyntaxNode, | ||
198 | to_delete: RangeInclusive<SyntaxElement>, | ||
164 | to_insert: &mut dyn Iterator<Item = SyntaxElement>, | 199 | to_insert: &mut dyn Iterator<Item = SyntaxElement>, |
165 | ) -> SyntaxNode { | 200 | ) -> SyntaxNode { |
166 | let start = position_of_child(parent, to_delete.start().clone()); | 201 | let start = position_of_child(parent, to_delete.start().clone()); |
@@ -185,14 +220,21 @@ pub fn replace_children( | |||
185 | /// to create a type-safe abstraction on top of it instead. | 220 | /// to create a type-safe abstraction on top of it instead. |
186 | pub fn replace_descendants( | 221 | pub fn replace_descendants( |
187 | parent: &SyntaxNode, | 222 | parent: &SyntaxNode, |
188 | map: &impl Fn(&SyntaxElement) -> Option<SyntaxElement>, | 223 | map: impl Fn(&SyntaxElement) -> Option<SyntaxElement>, |
224 | ) -> SyntaxNode { | ||
225 | _replace_descendants(parent, &map) | ||
226 | } | ||
227 | |||
228 | fn _replace_descendants( | ||
229 | parent: &SyntaxNode, | ||
230 | map: &dyn Fn(&SyntaxElement) -> Option<SyntaxElement>, | ||
189 | ) -> SyntaxNode { | 231 | ) -> SyntaxNode { |
190 | // FIXME: this could be made much faster. | 232 | // FIXME: this could be made much faster. |
191 | let new_children = parent.children_with_tokens().map(|it| go(map, it)).collect::<Vec<_>>(); | 233 | let new_children = parent.children_with_tokens().map(|it| go(map, it)).collect::<Vec<_>>(); |
192 | return with_children(parent, new_children); | 234 | return with_children(parent, new_children); |
193 | 235 | ||
194 | fn go( | 236 | fn go( |
195 | map: &impl Fn(&SyntaxElement) -> Option<SyntaxElement>, | 237 | map: &dyn Fn(&SyntaxElement) -> Option<SyntaxElement>, |
196 | element: SyntaxElement, | 238 | element: SyntaxElement, |
197 | ) -> NodeOrToken<rowan::GreenNode, rowan::GreenToken> { | 239 | ) -> NodeOrToken<rowan::GreenNode, rowan::GreenToken> { |
198 | if let Some(replacement) = map(&element) { | 240 | if let Some(replacement) = map(&element) { |
@@ -204,7 +246,7 @@ pub fn replace_descendants( | |||
204 | match element { | 246 | match element { |
205 | NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()), | 247 | NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()), |
206 | NodeOrToken::Node(it) => { | 248 | NodeOrToken::Node(it) => { |
207 | NodeOrToken::Node(replace_descendants(&it, map).green().clone()) | 249 | NodeOrToken::Node(_replace_descendants(&it, map).green().clone()) |
208 | } | 250 | } |
209 | } | 251 | } |
210 | } | 252 | } |
diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs index 0e78d8b63..d2630e9e9 100644 --- a/crates/ra_syntax/src/ast/edit.rs +++ b/crates/ra_syntax/src/ast/edit.rs | |||
@@ -276,7 +276,7 @@ pub fn replace_descendants<N: AstNode, D: AstNode>( | |||
276 | .into_iter() | 276 | .into_iter() |
277 | .map(|(from, to)| (from.syntax().clone().into(), to.syntax().clone().into())) | 277 | .map(|(from, to)| (from.syntax().clone().into(), to.syntax().clone().into())) |
278 | .collect::<FxHashMap<SyntaxElement, _>>(); | 278 | .collect::<FxHashMap<SyntaxElement, _>>(); |
279 | let new_syntax = algo::replace_descendants(parent.syntax(), &|n| map.get(n).cloned()); | 279 | let new_syntax = algo::replace_descendants(parent.syntax(), |n| map.get(n).cloned()); |
280 | N::cast(new_syntax).unwrap() | 280 | N::cast(new_syntax).unwrap() |
281 | } | 281 | } |
282 | 282 | ||
@@ -331,7 +331,7 @@ impl IndentLevel { | |||
331 | ) | 331 | ) |
332 | }) | 332 | }) |
333 | .collect(); | 333 | .collect(); |
334 | algo::replace_descendants(&node, &|n| replacements.get(n).cloned()) | 334 | algo::replace_descendants(&node, |n| replacements.get(n).cloned()) |
335 | } | 335 | } |
336 | 336 | ||
337 | pub fn decrease_indent<N: AstNode>(self, node: N) -> N { | 337 | pub fn decrease_indent<N: AstNode>(self, node: N) -> N { |
@@ -359,7 +359,7 @@ impl IndentLevel { | |||
359 | ) | 359 | ) |
360 | }) | 360 | }) |
361 | .collect(); | 361 | .collect(); |
362 | algo::replace_descendants(&node, &|n| replacements.get(n).cloned()) | 362 | algo::replace_descendants(&node, |n| replacements.get(n).cloned()) |
363 | } | 363 | } |
364 | } | 364 | } |
365 | 365 | ||
@@ -389,7 +389,7 @@ fn insert_children<N: AstNode>( | |||
389 | position: InsertPosition<SyntaxElement>, | 389 | position: InsertPosition<SyntaxElement>, |
390 | to_insert: impl IntoIterator<Item = SyntaxElement>, | 390 | to_insert: impl IntoIterator<Item = SyntaxElement>, |
391 | ) -> N { | 391 | ) -> N { |
392 | let new_syntax = algo::insert_children(parent.syntax(), position, &mut to_insert.into_iter()); | 392 | let new_syntax = algo::insert_children(parent.syntax(), position, to_insert); |
393 | N::cast(new_syntax).unwrap() | 393 | N::cast(new_syntax).unwrap() |
394 | } | 394 | } |
395 | 395 | ||
@@ -404,8 +404,7 @@ fn replace_children<N: AstNode>( | |||
404 | to_replace: RangeInclusive<SyntaxElement>, | 404 | to_replace: RangeInclusive<SyntaxElement>, |
405 | to_insert: impl IntoIterator<Item = SyntaxElement>, | 405 | to_insert: impl IntoIterator<Item = SyntaxElement>, |
406 | ) -> N { | 406 | ) -> N { |
407 | let new_syntax = | 407 | let new_syntax = algo::replace_children(parent.syntax(), to_replace, to_insert); |
408 | algo::replace_children(parent.syntax(), to_replace, &mut to_insert.into_iter()); | ||
409 | N::cast(new_syntax).unwrap() | 408 | N::cast(new_syntax).unwrap() |
410 | } | 409 | } |
411 | 410 | ||
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs index 7c20fcc10..3f11b747f 100644 --- a/crates/ra_syntax/src/ast/make.rs +++ b/crates/ra_syntax/src/ast/make.rs | |||
@@ -12,11 +12,14 @@ pub fn name_ref(text: &str) -> ast::NameRef { | |||
12 | ast_from_text(&format!("fn f() {{ {}; }}", text)) | 12 | ast_from_text(&format!("fn f() {{ {}; }}", text)) |
13 | } | 13 | } |
14 | 14 | ||
15 | pub fn path_from_name_ref(name_ref: ast::NameRef) -> ast::Path { | 15 | pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment { |
16 | path_from_text(&name_ref.syntax().to_string()) | 16 | ast_from_text(&format!("use {};", name_ref.syntax())) |
17 | } | 17 | } |
18 | pub fn path_qualified(qual: ast::Path, name_ref: ast::NameRef) -> ast::Path { | 18 | pub fn path_unqualified(segment: ast::PathSegment) -> ast::Path { |
19 | path_from_text(&format!("{}::{}", qual.syntax(), name_ref.syntax())) | 19 | path_from_text(&format!("use {}", segment.syntax())) |
20 | } | ||
21 | pub fn path_qualified(qual: ast::Path, segment: ast::PathSegment) -> ast::Path { | ||
22 | path_from_text(&format!("{}::{}", qual.syntax(), segment.syntax())) | ||
20 | } | 23 | } |
21 | fn path_from_text(text: &str) -> ast::Path { | 24 | fn path_from_text(text: &str) -> ast::Path { |
22 | ast_from_text(text) | 25 | ast_from_text(text) |
diff --git a/crates/ra_syntax/src/ast/tokens.rs b/crates/ra_syntax/src/ast/tokens.rs index ed8661faf..1a51b8d3b 100644 --- a/crates/ra_syntax/src/ast/tokens.rs +++ b/crates/ra_syntax/src/ast/tokens.rs | |||
@@ -110,6 +110,64 @@ impl Whitespace { | |||
110 | } | 110 | } |
111 | } | 111 | } |
112 | 112 | ||
113 | pub struct QuoteOffsets { | ||
114 | pub quotes: [TextRange; 2], | ||
115 | pub contents: TextRange, | ||
116 | } | ||
117 | |||
118 | impl QuoteOffsets { | ||
119 | fn new(literal: &str) -> Option<QuoteOffsets> { | ||
120 | let left_quote = literal.find('"')?; | ||
121 | let right_quote = literal.rfind('"')?; | ||
122 | if left_quote == right_quote { | ||
123 | // `literal` only contains one quote | ||
124 | return None; | ||
125 | } | ||
126 | |||
127 | let start = TextUnit::from(0); | ||
128 | let left_quote = TextUnit::from_usize(left_quote) + TextUnit::of_char('"'); | ||
129 | let right_quote = TextUnit::from_usize(right_quote); | ||
130 | let end = TextUnit::of_str(literal); | ||
131 | |||
132 | let res = QuoteOffsets { | ||
133 | quotes: [TextRange::from_to(start, left_quote), TextRange::from_to(right_quote, end)], | ||
134 | contents: TextRange::from_to(left_quote, right_quote), | ||
135 | }; | ||
136 | Some(res) | ||
137 | } | ||
138 | } | ||
139 | |||
140 | pub trait HasQuotes: AstToken { | ||
141 | fn quote_offsets(&self) -> Option<QuoteOffsets> { | ||
142 | let text = self.text().as_str(); | ||
143 | let offsets = QuoteOffsets::new(text)?; | ||
144 | let o = self.syntax().text_range().start(); | ||
145 | let offsets = QuoteOffsets { | ||
146 | quotes: [offsets.quotes[0] + o, offsets.quotes[1] + o], | ||
147 | contents: offsets.contents + o, | ||
148 | }; | ||
149 | Some(offsets) | ||
150 | } | ||
151 | fn open_quote_text_range(&self) -> Option<TextRange> { | ||
152 | self.quote_offsets().map(|it| it.quotes[0]) | ||
153 | } | ||
154 | |||
155 | fn close_quote_text_range(&self) -> Option<TextRange> { | ||
156 | self.quote_offsets().map(|it| it.quotes[1]) | ||
157 | } | ||
158 | |||
159 | fn text_range_between_quotes(&self) -> Option<TextRange> { | ||
160 | self.quote_offsets().map(|it| it.contents) | ||
161 | } | ||
162 | } | ||
163 | |||
164 | impl HasQuotes for String {} | ||
165 | impl HasQuotes for RawString {} | ||
166 | |||
167 | pub trait HasStringValue: HasQuotes { | ||
168 | fn value(&self) -> Option<std::string::String>; | ||
169 | } | ||
170 | |||
113 | pub struct String(SyntaxToken); | 171 | pub struct String(SyntaxToken); |
114 | 172 | ||
115 | impl AstToken for String { | 173 | impl AstToken for String { |
@@ -124,21 +182,16 @@ impl AstToken for String { | |||
124 | } | 182 | } |
125 | } | 183 | } |
126 | 184 | ||
127 | impl String { | 185 | impl HasStringValue for String { |
128 | pub fn value(&self) -> Option<std::string::String> { | 186 | fn value(&self) -> Option<std::string::String> { |
129 | let text = self.text().as_str(); | 187 | let text = self.text().as_str(); |
130 | let usual_string_range = find_usual_string_range(text)?; | 188 | let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; |
131 | let start_of_inside = usual_string_range.start().to_usize() + 1; | ||
132 | let end_of_inside = usual_string_range.end().to_usize(); | ||
133 | let inside_str = &text[start_of_inside..end_of_inside]; | ||
134 | 189 | ||
135 | let mut buf = std::string::String::with_capacity(inside_str.len()); | 190 | let mut buf = std::string::String::with_capacity(text.len()); |
136 | let mut has_error = false; | 191 | let mut has_error = false; |
137 | rustc_lexer::unescape::unescape_str(inside_str, &mut |_, unescaped_char| { | 192 | rustc_lexer::unescape::unescape_str(text, &mut |_, unescaped_char| match unescaped_char { |
138 | match unescaped_char { | 193 | Ok(c) => buf.push(c), |
139 | Ok(c) => buf.push(c), | 194 | Err(_) => has_error = true, |
140 | Err(_) => has_error = true, | ||
141 | } | ||
142 | }); | 195 | }); |
143 | 196 | ||
144 | if has_error { | 197 | if has_error { |
@@ -162,27 +215,18 @@ impl AstToken for RawString { | |||
162 | } | 215 | } |
163 | } | 216 | } |
164 | 217 | ||
165 | impl RawString { | 218 | impl HasStringValue for RawString { |
166 | pub fn value(&self) -> Option<std::string::String> { | 219 | fn value(&self) -> Option<std::string::String> { |
167 | let text = self.text().as_str(); | 220 | let text = self.text().as_str(); |
168 | let usual_string_range = find_usual_string_range(text)?; | 221 | let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; |
169 | let start_of_inside = usual_string_range.start().to_usize() + 1; | 222 | Some(text.to_string()) |
170 | let end_of_inside = usual_string_range.end().to_usize(); | 223 | } |
171 | let inside_str = &text[start_of_inside..end_of_inside]; | 224 | } |
172 | Some(inside_str.to_string()) | 225 | |
173 | } | 226 | impl RawString { |
174 | } | 227 | pub fn map_range_up(&self, range: TextRange) -> Option<TextRange> { |
175 | 228 | let contents_range = self.text_range_between_quotes()?; | |
176 | fn find_usual_string_range(s: &str) -> Option<TextRange> { | 229 | assert!(range.is_subrange(&TextRange::offset_len(0.into(), contents_range.len()))); |
177 | let left_quote = s.find('"')?; | 230 | Some(range + contents_range.start()) |
178 | let right_quote = s.rfind('"')?; | ||
179 | if left_quote == right_quote { | ||
180 | // `s` only contains one quote | ||
181 | None | ||
182 | } else { | ||
183 | Some(TextRange::from_to( | ||
184 | TextUnit::from(left_quote as u32), | ||
185 | TextUnit::from(right_quote as u32), | ||
186 | )) | ||
187 | } | 231 | } |
188 | } | 232 | } |
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0130_let_stmt.rs b/crates/ra_syntax/test_data/parser/inline/ok/0130_let_stmt.rs index 86803fe08..fa8ee49a2 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0130_let_stmt.rs +++ b/crates/ra_syntax/test_data/parser/inline/ok/0130_let_stmt.rs | |||
@@ -5,4 +5,5 @@ fn foo() { | |||
5 | let d: i32 = 92; | 5 | let d: i32 = 92; |
6 | let e: !; | 6 | let e: !; |
7 | let _: ! = {}; | 7 | let _: ! = {}; |
8 | let f = #[attr]||{}; | ||
8 | } | 9 | } |
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0130_let_stmt.txt b/crates/ra_syntax/test_data/parser/inline/ok/0130_let_stmt.txt index 97166cfd5..17739dfbd 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0130_let_stmt.txt +++ b/crates/ra_syntax/test_data/parser/inline/ok/0130_let_stmt.txt | |||
@@ -1,5 +1,5 @@ | |||
1 | SOURCE_FILE@[0; 110) | 1 | SOURCE_FILE@[0; 135) |
2 | FN_DEF@[0; 109) | 2 | FN_DEF@[0; 134) |
3 | FN_KW@[0; 2) "fn" | 3 | FN_KW@[0; 2) "fn" |
4 | WHITESPACE@[2; 3) " " | 4 | WHITESPACE@[2; 3) " " |
5 | NAME@[3; 6) | 5 | NAME@[3; 6) |
@@ -8,8 +8,8 @@ SOURCE_FILE@[0; 110) | |||
8 | L_PAREN@[6; 7) "(" | 8 | L_PAREN@[6; 7) "(" |
9 | R_PAREN@[7; 8) ")" | 9 | R_PAREN@[7; 8) ")" |
10 | WHITESPACE@[8; 9) " " | 10 | WHITESPACE@[8; 9) " " |
11 | BLOCK_EXPR@[9; 109) | 11 | BLOCK_EXPR@[9; 134) |
12 | BLOCK@[9; 109) | 12 | BLOCK@[9; 134) |
13 | L_CURLY@[9; 10) "{" | 13 | L_CURLY@[9; 10) "{" |
14 | WHITESPACE@[10; 15) "\n " | 14 | WHITESPACE@[10; 15) "\n " |
15 | LET_STMT@[15; 21) | 15 | LET_STMT@[15; 21) |
@@ -97,6 +97,33 @@ SOURCE_FILE@[0; 110) | |||
97 | L_CURLY@[104; 105) "{" | 97 | L_CURLY@[104; 105) "{" |
98 | R_CURLY@[105; 106) "}" | 98 | R_CURLY@[105; 106) "}" |
99 | SEMI@[106; 107) ";" | 99 | SEMI@[106; 107) ";" |
100 | WHITESPACE@[107; 108) "\n" | 100 | WHITESPACE@[107; 112) "\n " |
101 | R_CURLY@[108; 109) "}" | 101 | LET_STMT@[112; 132) |
102 | WHITESPACE@[109; 110) "\n" | 102 | LET_KW@[112; 115) "let" |
103 | WHITESPACE@[115; 116) " " | ||
104 | BIND_PAT@[116; 117) | ||
105 | NAME@[116; 117) | ||
106 | IDENT@[116; 117) "f" | ||
107 | WHITESPACE@[117; 118) " " | ||
108 | EQ@[118; 119) "=" | ||
109 | WHITESPACE@[119; 120) " " | ||
110 | LAMBDA_EXPR@[120; 131) | ||
111 | ATTR@[120; 127) | ||
112 | POUND@[120; 121) "#" | ||
113 | L_BRACK@[121; 122) "[" | ||
114 | PATH@[122; 126) | ||
115 | PATH_SEGMENT@[122; 126) | ||
116 | NAME_REF@[122; 126) | ||
117 | IDENT@[122; 126) "attr" | ||
118 | R_BRACK@[126; 127) "]" | ||
119 | PARAM_LIST@[127; 129) | ||
120 | PIPE@[127; 128) "|" | ||
121 | PIPE@[128; 129) "|" | ||
122 | BLOCK_EXPR@[129; 131) | ||
123 | BLOCK@[129; 131) | ||
124 | L_CURLY@[129; 130) "{" | ||
125 | R_CURLY@[130; 131) "}" | ||
126 | SEMI@[131; 132) ";" | ||
127 | WHITESPACE@[132; 133) "\n" | ||
128 | R_CURLY@[133; 134) "}" | ||
129 | WHITESPACE@[134; 135) "\n" | ||