diff options
Diffstat (limited to 'crates/syntax')
-rw-r--r-- | crates/syntax/src/ast/edit.rs | 151 | ||||
-rw-r--r-- | crates/syntax/src/ast/edit_in_place.rs | 72 | ||||
-rw-r--r-- | crates/syntax/src/ast/make.rs | 2 |
3 files changed, 72 insertions, 153 deletions
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs index 10ec94cd2..7e4b8252e 100644 --- a/crates/syntax/src/ast/edit.rs +++ b/crates/syntax/src/ast/edit.rs | |||
@@ -80,81 +80,6 @@ where | |||
80 | } | 80 | } |
81 | } | 81 | } |
82 | 82 | ||
83 | impl ast::Impl { | ||
84 | #[must_use] | ||
85 | pub fn with_assoc_item_list(&self, items: ast::AssocItemList) -> ast::Impl { | ||
86 | let mut to_insert: ArrayVec<SyntaxElement, 2> = ArrayVec::new(); | ||
87 | if let Some(old_items) = self.assoc_item_list() { | ||
88 | let to_replace: SyntaxElement = old_items.syntax().clone().into(); | ||
89 | to_insert.push(items.syntax().clone().into()); | ||
90 | self.replace_children(single_node(to_replace), to_insert) | ||
91 | } else { | ||
92 | to_insert.push(make::tokens::single_space().into()); | ||
93 | to_insert.push(items.syntax().clone().into()); | ||
94 | self.insert_children(InsertPosition::Last, to_insert) | ||
95 | } | ||
96 | } | ||
97 | } | ||
98 | |||
99 | impl ast::AssocItemList { | ||
100 | #[must_use] | ||
101 | pub fn append_items( | ||
102 | &self, | ||
103 | items: impl IntoIterator<Item = ast::AssocItem>, | ||
104 | ) -> ast::AssocItemList { | ||
105 | let mut res = self.clone(); | ||
106 | if !self.syntax().text().contains_char('\n') { | ||
107 | res = make_multiline(res); | ||
108 | } | ||
109 | items.into_iter().for_each(|it| res = res.append_item(it)); | ||
110 | res.fixup_trailing_whitespace().unwrap_or(res) | ||
111 | } | ||
112 | |||
113 | #[must_use] | ||
114 | pub fn append_item(&self, item: ast::AssocItem) -> ast::AssocItemList { | ||
115 | let (indent, position, whitespace) = match self.assoc_items().last() { | ||
116 | Some(it) => ( | ||
117 | leading_indent(it.syntax()).unwrap_or_default().to_string(), | ||
118 | InsertPosition::After(it.syntax().clone().into()), | ||
119 | "\n\n", | ||
120 | ), | ||
121 | None => match self.l_curly_token() { | ||
122 | Some(it) => ( | ||
123 | " ".to_string() + &leading_indent(self.syntax()).unwrap_or_default(), | ||
124 | InsertPosition::After(it.into()), | ||
125 | "\n", | ||
126 | ), | ||
127 | None => return self.clone(), | ||
128 | }, | ||
129 | }; | ||
130 | let ws = tokens::WsBuilder::new(&format!("{}{}", whitespace, indent)); | ||
131 | let to_insert: ArrayVec<SyntaxElement, 2> = | ||
132 | [ws.ws().into(), item.syntax().clone().into()].into(); | ||
133 | self.insert_children(position, to_insert) | ||
134 | } | ||
135 | |||
136 | /// Remove extra whitespace between last item and closing curly brace. | ||
137 | fn fixup_trailing_whitespace(&self) -> Option<ast::AssocItemList> { | ||
138 | let first_token_after_items = | ||
139 | self.assoc_items().last()?.syntax().next_sibling_or_token()?; | ||
140 | let last_token_before_curly = self.r_curly_token()?.prev_sibling_or_token()?; | ||
141 | if last_token_before_curly != first_token_after_items { | ||
142 | // there is something more between last item and | ||
143 | // right curly than just whitespace - bail out | ||
144 | return None; | ||
145 | } | ||
146 | let whitespace = | ||
147 | last_token_before_curly.clone().into_token().and_then(ast::Whitespace::cast)?; | ||
148 | let text = whitespace.syntax().text(); | ||
149 | let newline = text.rfind('\n')?; | ||
150 | let keep = tokens::WsBuilder::new(&text[newline..]); | ||
151 | Some(self.replace_children( | ||
152 | first_token_after_items..=last_token_before_curly, | ||
153 | std::iter::once(keep.ws().into()), | ||
154 | )) | ||
155 | } | ||
156 | } | ||
157 | |||
158 | impl ast::RecordExprFieldList { | 83 | impl ast::RecordExprFieldList { |
159 | #[must_use] | 84 | #[must_use] |
160 | pub fn append_field(&self, field: &ast::RecordExprField) -> ast::RecordExprFieldList { | 85 | pub fn append_field(&self, field: &ast::RecordExprField) -> ast::RecordExprFieldList { |
@@ -246,21 +171,6 @@ impl ast::TypeAlias { | |||
246 | } | 171 | } |
247 | } | 172 | } |
248 | 173 | ||
249 | impl ast::TypeParam { | ||
250 | #[must_use] | ||
251 | pub fn remove_bounds(&self) -> ast::TypeParam { | ||
252 | let colon = match self.colon_token() { | ||
253 | Some(it) => it, | ||
254 | None => return self.clone(), | ||
255 | }; | ||
256 | let end = match self.type_bound_list() { | ||
257 | Some(it) => it.syntax().clone().into(), | ||
258 | None => colon.clone().into(), | ||
259 | }; | ||
260 | self.replace_children(colon.into()..=end, iter::empty()) | ||
261 | } | ||
262 | } | ||
263 | |||
264 | impl ast::Path { | 174 | impl ast::Path { |
265 | #[must_use] | 175 | #[must_use] |
266 | pub fn with_segment(&self, segment: ast::PathSegment) -> ast::Path { | 176 | pub fn with_segment(&self, segment: ast::PathSegment) -> ast::Path { |
@@ -411,61 +321,6 @@ impl ast::MatchArmList { | |||
411 | } | 321 | } |
412 | } | 322 | } |
413 | 323 | ||
414 | impl ast::GenericParamList { | ||
415 | #[must_use] | ||
416 | pub fn append_params( | ||
417 | &self, | ||
418 | params: impl IntoIterator<Item = ast::GenericParam>, | ||
419 | ) -> ast::GenericParamList { | ||
420 | let mut res = self.clone(); | ||
421 | params.into_iter().for_each(|it| res = res.append_param(it)); | ||
422 | res | ||
423 | } | ||
424 | |||
425 | #[must_use] | ||
426 | pub fn append_param(&self, item: ast::GenericParam) -> ast::GenericParamList { | ||
427 | let space = tokens::single_space(); | ||
428 | |||
429 | let mut to_insert: ArrayVec<SyntaxElement, 4> = ArrayVec::new(); | ||
430 | if self.generic_params().next().is_some() { | ||
431 | to_insert.push(space.into()); | ||
432 | } | ||
433 | to_insert.push(item.syntax().clone().into()); | ||
434 | |||
435 | macro_rules! after_l_angle { | ||
436 | () => {{ | ||
437 | let anchor = match self.l_angle_token() { | ||
438 | Some(it) => it.into(), | ||
439 | None => return self.clone(), | ||
440 | }; | ||
441 | InsertPosition::After(anchor) | ||
442 | }}; | ||
443 | } | ||
444 | |||
445 | macro_rules! after_field { | ||
446 | ($anchor:expr) => { | ||
447 | if let Some(comma) = $anchor | ||
448 | .syntax() | ||
449 | .siblings_with_tokens(Direction::Next) | ||
450 | .find(|it| it.kind() == T![,]) | ||
451 | { | ||
452 | InsertPosition::After(comma) | ||
453 | } else { | ||
454 | to_insert.insert(0, make::token(T![,]).into()); | ||
455 | InsertPosition::After($anchor.syntax().clone().into()) | ||
456 | } | ||
457 | }; | ||
458 | } | ||
459 | |||
460 | let position = match self.generic_params().last() { | ||
461 | Some(it) => after_field!(it), | ||
462 | None => after_l_angle!(), | ||
463 | }; | ||
464 | |||
465 | self.insert_children(position, to_insert) | ||
466 | } | ||
467 | } | ||
468 | |||
469 | #[must_use] | 324 | #[must_use] |
470 | pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N { | 325 | pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N { |
471 | N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap() | 326 | N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap() |
@@ -516,6 +371,12 @@ impl ops::Add<u8> for IndentLevel { | |||
516 | } | 371 | } |
517 | 372 | ||
518 | impl IndentLevel { | 373 | impl IndentLevel { |
374 | pub fn single() -> IndentLevel { | ||
375 | IndentLevel(0) | ||
376 | } | ||
377 | pub fn is_zero(&self) -> bool { | ||
378 | self.0 == 0 | ||
379 | } | ||
519 | pub fn from_element(element: &SyntaxElement) -> IndentLevel { | 380 | pub fn from_element(element: &SyntaxElement) -> IndentLevel { |
520 | match element { | 381 | match element { |
521 | rowan::NodeOrToken::Node(it) => IndentLevel::from_node(it), | 382 | rowan::NodeOrToken::Node(it) => IndentLevel::from_node(it), |
diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs index 168355555..9812e00c9 100644 --- a/crates/syntax/src/ast/edit_in_place.rs +++ b/crates/syntax/src/ast/edit_in_place.rs | |||
@@ -2,11 +2,16 @@ | |||
2 | 2 | ||
3 | use std::iter::empty; | 3 | use std::iter::empty; |
4 | 4 | ||
5 | use parser::T; | 5 | use parser::{SyntaxKind, T}; |
6 | use rowan::SyntaxElement; | ||
6 | 7 | ||
7 | use crate::{ | 8 | use crate::{ |
8 | algo::neighbor, | 9 | algo::neighbor, |
9 | ast::{self, edit::AstNodeEdit, make, GenericParamsOwner, WhereClause}, | 10 | ast::{ |
11 | self, | ||
12 | edit::{AstNodeEdit, IndentLevel}, | ||
13 | make, GenericParamsOwner, | ||
14 | }, | ||
10 | ted::{self, Position}, | 15 | ted::{self, Position}, |
11 | AstNode, AstToken, Direction, | 16 | AstNode, AstToken, Direction, |
12 | }; | 17 | }; |
@@ -37,7 +42,7 @@ impl GenericParamsOwnerEdit for ast::Fn { | |||
37 | } | 42 | } |
38 | } | 43 | } |
39 | 44 | ||
40 | fn get_or_create_where_clause(&self) -> WhereClause { | 45 | fn get_or_create_where_clause(&self) -> ast::WhereClause { |
41 | if self.where_clause().is_none() { | 46 | if self.where_clause().is_none() { |
42 | let position = if let Some(ty) = self.ret_type() { | 47 | let position = if let Some(ty) = self.ret_type() { |
43 | Position::after(ty.syntax()) | 48 | Position::after(ty.syntax()) |
@@ -67,7 +72,7 @@ impl GenericParamsOwnerEdit for ast::Impl { | |||
67 | } | 72 | } |
68 | } | 73 | } |
69 | 74 | ||
70 | fn get_or_create_where_clause(&self) -> WhereClause { | 75 | fn get_or_create_where_clause(&self) -> ast::WhereClause { |
71 | if self.where_clause().is_none() { | 76 | if self.where_clause().is_none() { |
72 | let position = if let Some(items) = self.assoc_item_list() { | 77 | let position = if let Some(items) = self.assoc_item_list() { |
73 | Position::before(items.syntax()) | 78 | Position::before(items.syntax()) |
@@ -97,7 +102,7 @@ impl GenericParamsOwnerEdit for ast::Trait { | |||
97 | } | 102 | } |
98 | } | 103 | } |
99 | 104 | ||
100 | fn get_or_create_where_clause(&self) -> WhereClause { | 105 | fn get_or_create_where_clause(&self) -> ast::WhereClause { |
101 | if self.where_clause().is_none() { | 106 | if self.where_clause().is_none() { |
102 | let position = if let Some(items) = self.assoc_item_list() { | 107 | let position = if let Some(items) = self.assoc_item_list() { |
103 | Position::before(items.syntax()) | 108 | Position::before(items.syntax()) |
@@ -127,7 +132,7 @@ impl GenericParamsOwnerEdit for ast::Struct { | |||
127 | } | 132 | } |
128 | } | 133 | } |
129 | 134 | ||
130 | fn get_or_create_where_clause(&self) -> WhereClause { | 135 | fn get_or_create_where_clause(&self) -> ast::WhereClause { |
131 | if self.where_clause().is_none() { | 136 | if self.where_clause().is_none() { |
132 | let tfl = self.field_list().and_then(|fl| match fl { | 137 | let tfl = self.field_list().and_then(|fl| match fl { |
133 | ast::FieldList::RecordFieldList(_) => None, | 138 | ast::FieldList::RecordFieldList(_) => None, |
@@ -165,7 +170,7 @@ impl GenericParamsOwnerEdit for ast::Enum { | |||
165 | } | 170 | } |
166 | } | 171 | } |
167 | 172 | ||
168 | fn get_or_create_where_clause(&self) -> WhereClause { | 173 | fn get_or_create_where_clause(&self) -> ast::WhereClause { |
169 | if self.where_clause().is_none() { | 174 | if self.where_clause().is_none() { |
170 | let position = if let Some(gpl) = self.generic_param_list() { | 175 | let position = if let Some(gpl) = self.generic_param_list() { |
171 | Position::after(gpl.syntax()) | 176 | Position::after(gpl.syntax()) |
@@ -272,6 +277,59 @@ impl ast::Use { | |||
272 | } | 277 | } |
273 | } | 278 | } |
274 | 279 | ||
280 | impl ast::Impl { | ||
281 | pub fn get_or_create_assoc_item_list(&self) -> ast::AssocItemList { | ||
282 | if self.assoc_item_list().is_none() { | ||
283 | let assoc_item_list = make::assoc_item_list().clone_for_update(); | ||
284 | ted::append_child(self.syntax(), assoc_item_list.syntax()); | ||
285 | } | ||
286 | self.assoc_item_list().unwrap() | ||
287 | } | ||
288 | } | ||
289 | |||
290 | impl ast::AssocItemList { | ||
291 | pub fn add_item(&self, item: ast::AssocItem) { | ||
292 | let (indent, position, whitespace) = match self.assoc_items().last() { | ||
293 | Some(last_item) => ( | ||
294 | IndentLevel::from_node(last_item.syntax()), | ||
295 | Position::after(last_item.syntax()), | ||
296 | "\n\n", | ||
297 | ), | ||
298 | None => match self.l_curly_token() { | ||
299 | Some(l_curly) => { | ||
300 | self.normalize_ws_between_braces(); | ||
301 | (IndentLevel::from_token(&l_curly) + 1, Position::after(&l_curly), "\n") | ||
302 | } | ||
303 | None => (IndentLevel::single(), Position::last_child_of(self.syntax()), "\n"), | ||
304 | }, | ||
305 | }; | ||
306 | let elements: Vec<SyntaxElement<_>> = vec![ | ||
307 | make::tokens::whitespace(&format!("{}{}", whitespace, indent)).into(), | ||
308 | item.syntax().clone().into(), | ||
309 | ]; | ||
310 | ted::insert_all(position, elements); | ||
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 | } | ||
332 | |||
275 | #[cfg(test)] | 333 | #[cfg(test)] |
276 | mod tests { | 334 | mod tests { |
277 | use std::fmt; | 335 | use std::fmt; |
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index de04c8620..d13926ded 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs | |||
@@ -99,7 +99,7 @@ fn ty_from_text(text: &str) -> ast::Type { | |||
99 | } | 99 | } |
100 | 100 | ||
101 | pub fn assoc_item_list() -> ast::AssocItemList { | 101 | pub fn assoc_item_list() -> ast::AssocItemList { |
102 | ast_from_text("impl C for D {};") | 102 | ast_from_text("impl C for D {}") |
103 | } | 103 | } |
104 | 104 | ||
105 | pub fn impl_trait(trait_: ast::Path, ty: ast::Path) -> ast::Impl { | 105 | pub fn impl_trait(trait_: ast::Path, ty: ast::Path) -> ast::Impl { |