diff options
Diffstat (limited to 'crates/syntax')
-rw-r--r-- | crates/syntax/src/ast/edit.rs | 78 | ||||
-rw-r--r-- | crates/syntax/src/ast/make.rs | 27 |
2 files changed, 99 insertions, 6 deletions
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs index 060b20966..8b1c65dd6 100644 --- a/crates/syntax/src/ast/edit.rs +++ b/crates/syntax/src/ast/edit.rs | |||
@@ -13,7 +13,7 @@ use crate::{ | |||
13 | ast::{ | 13 | ast::{ |
14 | self, | 14 | self, |
15 | make::{self, tokens}, | 15 | make::{self, tokens}, |
16 | AstNode, TypeBoundsOwner, | 16 | AstNode, GenericParamsOwner, NameOwner, TypeBoundsOwner, |
17 | }, | 17 | }, |
18 | AstToken, Direction, InsertPosition, SmolStr, SyntaxElement, SyntaxKind, | 18 | AstToken, Direction, InsertPosition, SmolStr, SyntaxElement, SyntaxKind, |
19 | SyntaxKind::{ATTR, COMMENT, WHITESPACE}, | 19 | SyntaxKind::{ATTR, COMMENT, WHITESPACE}, |
@@ -46,6 +46,19 @@ impl ast::Fn { | |||
46 | to_insert.push(body.syntax().clone().into()); | 46 | to_insert.push(body.syntax().clone().into()); |
47 | self.replace_children(single_node(old_body_or_semi), to_insert) | 47 | self.replace_children(single_node(old_body_or_semi), to_insert) |
48 | } | 48 | } |
49 | |||
50 | #[must_use] | ||
51 | pub fn with_generic_param_list(&self, generic_args: ast::GenericParamList) -> ast::Fn { | ||
52 | if let Some(old) = self.generic_param_list() { | ||
53 | return self.replace_descendant(old, generic_args); | ||
54 | } | ||
55 | |||
56 | let anchor = self.name().expect("The function must have a name").syntax().clone(); | ||
57 | |||
58 | let mut to_insert: ArrayVec<[SyntaxElement; 1]> = ArrayVec::new(); | ||
59 | to_insert.push(generic_args.syntax().clone().into()); | ||
60 | self.insert_children(InsertPosition::After(anchor.into()), to_insert) | ||
61 | } | ||
49 | } | 62 | } |
50 | 63 | ||
51 | fn make_multiline<N>(node: N) -> N | 64 | fn make_multiline<N>(node: N) -> N |
@@ -260,16 +273,16 @@ impl ast::Path { | |||
260 | 273 | ||
261 | impl ast::PathSegment { | 274 | impl ast::PathSegment { |
262 | #[must_use] | 275 | #[must_use] |
263 | pub fn with_type_args(&self, type_args: ast::GenericArgList) -> ast::PathSegment { | 276 | pub fn with_generic_args(&self, type_args: ast::GenericArgList) -> ast::PathSegment { |
264 | self._with_type_args(type_args, false) | 277 | self._with_generic_args(type_args, false) |
265 | } | 278 | } |
266 | 279 | ||
267 | #[must_use] | 280 | #[must_use] |
268 | pub fn with_turbo_fish(&self, type_args: ast::GenericArgList) -> ast::PathSegment { | 281 | pub fn with_turbo_fish(&self, type_args: ast::GenericArgList) -> ast::PathSegment { |
269 | self._with_type_args(type_args, true) | 282 | self._with_generic_args(type_args, true) |
270 | } | 283 | } |
271 | 284 | ||
272 | fn _with_type_args(&self, type_args: ast::GenericArgList, turbo: bool) -> ast::PathSegment { | 285 | fn _with_generic_args(&self, type_args: ast::GenericArgList, turbo: bool) -> ast::PathSegment { |
273 | if let Some(old) = self.generic_arg_list() { | 286 | if let Some(old) = self.generic_arg_list() { |
274 | return self.replace_children( | 287 | return self.replace_children( |
275 | single_node(old.syntax().clone()), | 288 | single_node(old.syntax().clone()), |
@@ -459,6 +472,61 @@ impl ast::MatchArmList { | |||
459 | } | 472 | } |
460 | } | 473 | } |
461 | 474 | ||
475 | impl ast::GenericParamList { | ||
476 | #[must_use] | ||
477 | pub fn append_params( | ||
478 | &self, | ||
479 | params: impl IntoIterator<Item = ast::GenericParam>, | ||
480 | ) -> ast::GenericParamList { | ||
481 | let mut res = self.clone(); | ||
482 | params.into_iter().for_each(|it| res = res.append_param(it)); | ||
483 | res | ||
484 | } | ||
485 | |||
486 | #[must_use] | ||
487 | pub fn append_param(&self, item: ast::GenericParam) -> ast::GenericParamList { | ||
488 | let space = tokens::single_space(); | ||
489 | |||
490 | let mut to_insert: ArrayVec<[SyntaxElement; 4]> = ArrayVec::new(); | ||
491 | if self.generic_params().next().is_some() { | ||
492 | to_insert.push(space.into()); | ||
493 | } | ||
494 | to_insert.push(item.syntax().clone().into()); | ||
495 | |||
496 | macro_rules! after_l_angle { | ||
497 | () => {{ | ||
498 | let anchor = match self.l_angle_token() { | ||
499 | Some(it) => it.into(), | ||
500 | None => return self.clone(), | ||
501 | }; | ||
502 | InsertPosition::After(anchor) | ||
503 | }}; | ||
504 | } | ||
505 | |||
506 | macro_rules! after_field { | ||
507 | ($anchor:expr) => { | ||
508 | if let Some(comma) = $anchor | ||
509 | .syntax() | ||
510 | .siblings_with_tokens(Direction::Next) | ||
511 | .find(|it| it.kind() == T![,]) | ||
512 | { | ||
513 | InsertPosition::After(comma) | ||
514 | } else { | ||
515 | to_insert.insert(0, make::token(T![,]).into()); | ||
516 | InsertPosition::After($anchor.syntax().clone().into()) | ||
517 | } | ||
518 | }; | ||
519 | }; | ||
520 | |||
521 | let position = match self.generic_params().last() { | ||
522 | Some(it) => after_field!(it), | ||
523 | None => after_l_angle!(), | ||
524 | }; | ||
525 | |||
526 | self.insert_children(position, to_insert) | ||
527 | } | ||
528 | } | ||
529 | |||
462 | #[must_use] | 530 | #[must_use] |
463 | pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N { | 531 | pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N { |
464 | N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap() | 532 | N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap() |
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index c2c938ad1..25e8a359d 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs | |||
@@ -294,6 +294,21 @@ pub fn param_list(pats: impl IntoIterator<Item = ast::Param>) -> ast::ParamList | |||
294 | ast_from_text(&format!("fn f({}) {{ }}", args)) | 294 | ast_from_text(&format!("fn f({}) {{ }}", args)) |
295 | } | 295 | } |
296 | 296 | ||
297 | pub fn generic_param(name: String, ty: Option<ast::TypeBoundList>) -> ast::GenericParam { | ||
298 | let bound = match ty { | ||
299 | Some(it) => format!(": {}", it), | ||
300 | None => String::new(), | ||
301 | }; | ||
302 | ast_from_text(&format!("fn f<{}{}>() {{ }}", name, bound)) | ||
303 | } | ||
304 | |||
305 | pub fn generic_param_list( | ||
306 | pats: impl IntoIterator<Item = ast::GenericParam>, | ||
307 | ) -> ast::GenericParamList { | ||
308 | let args = pats.into_iter().join(", "); | ||
309 | ast_from_text(&format!("fn f<{}>() {{ }}", args)) | ||
310 | } | ||
311 | |||
297 | pub fn visibility_pub_crate() -> ast::Visibility { | 312 | pub fn visibility_pub_crate() -> ast::Visibility { |
298 | ast_from_text("pub(crate) struct S") | 313 | ast_from_text("pub(crate) struct S") |
299 | } | 314 | } |
@@ -339,7 +354,7 @@ pub mod tokens { | |||
339 | use crate::{ast, AstNode, Parse, SourceFile, SyntaxKind::*, SyntaxToken}; | 354 | use crate::{ast, AstNode, Parse, SourceFile, SyntaxKind::*, SyntaxToken}; |
340 | 355 | ||
341 | pub(super) static SOURCE_FILE: Lazy<Parse<SourceFile>> = | 356 | pub(super) static SOURCE_FILE: Lazy<Parse<SourceFile>> = |
342 | Lazy::new(|| SourceFile::parse("const C: <()>::Item = (1 != 1, 2 == 2, !true)\n;")); | 357 | Lazy::new(|| SourceFile::parse("const C: <()>::Item = (1 != 1, 2 == 2, !true)\n;\n\n")); |
343 | 358 | ||
344 | pub fn single_space() -> SyntaxToken { | 359 | pub fn single_space() -> SyntaxToken { |
345 | SOURCE_FILE | 360 | SOURCE_FILE |
@@ -379,6 +394,16 @@ pub mod tokens { | |||
379 | .unwrap() | 394 | .unwrap() |
380 | } | 395 | } |
381 | 396 | ||
397 | pub fn blank_line() -> SyntaxToken { | ||
398 | SOURCE_FILE | ||
399 | .tree() | ||
400 | .syntax() | ||
401 | .descendants_with_tokens() | ||
402 | .filter_map(|it| it.into_token()) | ||
403 | .find(|it| it.kind() == WHITESPACE && it.text().as_str() == "\n\n") | ||
404 | .unwrap() | ||
405 | } | ||
406 | |||
382 | pub struct WsBuilder(SourceFile); | 407 | pub struct WsBuilder(SourceFile); |
383 | 408 | ||
384 | impl WsBuilder { | 409 | impl WsBuilder { |