diff options
Diffstat (limited to 'crates/syntax')
-rw-r--r-- | crates/syntax/src/ast/edit_in_place.rs | 19 | ||||
-rw-r--r-- | crates/syntax/src/ast/make.rs | 111 |
2 files changed, 80 insertions, 50 deletions
diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs index 04f97f368..168355555 100644 --- a/crates/syntax/src/ast/edit_in_place.rs +++ b/crates/syntax/src/ast/edit_in_place.rs | |||
@@ -195,18 +195,13 @@ impl ast::GenericParamList { | |||
195 | pub fn add_generic_param(&self, generic_param: ast::GenericParam) { | 195 | pub fn add_generic_param(&self, generic_param: ast::GenericParam) { |
196 | match self.generic_params().last() { | 196 | match self.generic_params().last() { |
197 | Some(last_param) => { | 197 | Some(last_param) => { |
198 | let mut elems = Vec::new(); | 198 | let position = Position::after(last_param.syntax()); |
199 | if !last_param | 199 | let elements = vec![ |
200 | .syntax() | 200 | make::token(T![,]).into(), |
201 | .siblings_with_tokens(Direction::Next) | 201 | make::tokens::single_space().into(), |
202 | .any(|it| it.kind() == T![,]) | 202 | generic_param.syntax().clone().into(), |
203 | { | 203 | ]; |
204 | elems.push(make::token(T![,]).into()); | 204 | ted::insert_all(position, elements); |
205 | elems.push(make::tokens::single_space().into()); | ||
206 | }; | ||
207 | elems.push(generic_param.syntax().clone().into()); | ||
208 | let after_last_param = Position::after(last_param.syntax()); | ||
209 | ted::insert_all(after_last_param, elems); | ||
210 | } | 205 | } |
211 | None => { | 206 | None => { |
212 | let after_l_angle = Position::after(self.l_angle_token().unwrap()); | 207 | let after_l_angle = Position::after(self.l_angle_token().unwrap()); |
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 5a6687397..1998ad1f6 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs | |||
@@ -3,25 +3,55 @@ | |||
3 | //! | 3 | //! |
4 | //! Note that all functions here intended to be stupid constructors, which just | 4 | //! Note that all functions here intended to be stupid constructors, which just |
5 | //! assemble a finish node from immediate children. If you want to do something | 5 | //! assemble a finish node from immediate children. If you want to do something |
6 | //! smarter than that, it probably doesn't belong in this module. | 6 | //! smarter than that, it belongs to the `ext` submodule. |
7 | //! | 7 | //! |
8 | //! Keep in mind that `from_text` functions should be kept private. The public | 8 | //! Keep in mind that `from_text` functions should be kept private. The public |
9 | //! API should require to assemble every node piecewise. The trick of | 9 | //! API should require to assemble every node piecewise. The trick of |
10 | //! `parse(format!())` we use internally is an implementation detail -- long | 10 | //! `parse(format!())` we use internally is an implementation detail -- long |
11 | //! term, it will be replaced with direct tree manipulation. | 11 | //! term, it will be replaced with direct tree manipulation. |
12 | use itertools::Itertools; | 12 | use itertools::Itertools; |
13 | use stdx::format_to; | 13 | use stdx::{format_to, never}; |
14 | 14 | ||
15 | use crate::{ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, SyntaxToken}; | 15 | use crate::{ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, SyntaxToken}; |
16 | 16 | ||
17 | /// While the parent module defines basic atomic "constructors", the `ext` | ||
18 | /// module defines shortcuts for common things. | ||
19 | /// | ||
20 | /// It's named `ext` rather than `shortcuts` just to keep it short. | ||
21 | pub mod ext { | ||
22 | use super::*; | ||
23 | |||
24 | pub fn ident_path(ident: &str) -> ast::Path { | ||
25 | path_unqualified(path_segment(name_ref(ident))) | ||
26 | } | ||
27 | |||
28 | pub fn expr_unreachable() -> ast::Expr { | ||
29 | expr_from_text("unreachable!()") | ||
30 | } | ||
31 | pub fn expr_todo() -> ast::Expr { | ||
32 | expr_from_text("todo!()") | ||
33 | } | ||
34 | pub fn empty_block_expr() -> ast::BlockExpr { | ||
35 | block_expr(None, None) | ||
36 | } | ||
37 | |||
38 | pub fn ty_bool() -> ast::Type { | ||
39 | ty_path(ident_path("bool")) | ||
40 | } | ||
41 | pub fn ty_option(t: ast::Type) -> ast::Type { | ||
42 | ty_from_text(&format!("Option<{}>", t)) | ||
43 | } | ||
44 | pub fn ty_result(t: ast::Type, e: ast::Type) -> ast::Type { | ||
45 | ty_from_text(&format!("Result<{}, {}>", t, e)) | ||
46 | } | ||
47 | } | ||
48 | |||
17 | pub fn name(text: &str) -> ast::Name { | 49 | pub fn name(text: &str) -> ast::Name { |
18 | ast_from_text(&format!("mod {}{};", raw_ident_esc(text), text)) | 50 | ast_from_text(&format!("mod {}{};", raw_ident_esc(text), text)) |
19 | } | 51 | } |
20 | |||
21 | pub fn name_ref(text: &str) -> ast::NameRef { | 52 | pub fn name_ref(text: &str) -> ast::NameRef { |
22 | ast_from_text(&format!("fn f() {{ {}{}; }}", raw_ident_esc(text), text)) | 53 | ast_from_text(&format!("fn f() {{ {}{}; }}", raw_ident_esc(text), text)) |
23 | } | 54 | } |
24 | |||
25 | fn raw_ident_esc(ident: &str) -> &'static str { | 55 | fn raw_ident_esc(ident: &str) -> &'static str { |
26 | let is_keyword = parser::SyntaxKind::from_keyword(ident).is_some(); | 56 | let is_keyword = parser::SyntaxKind::from_keyword(ident).is_some(); |
27 | if is_keyword && !matches!(ident, "self" | "crate" | "super" | "Self") { | 57 | if is_keyword && !matches!(ident, "self" | "crate" | "super" | "Self") { |
@@ -31,13 +61,23 @@ fn raw_ident_esc(ident: &str) -> &'static str { | |||
31 | } | 61 | } |
32 | } | 62 | } |
33 | 63 | ||
64 | pub fn lifetime(text: &str) -> ast::Lifetime { | ||
65 | let mut text = text; | ||
66 | let tmp; | ||
67 | if never!(!text.starts_with('\'')) { | ||
68 | tmp = format!("'{}", text); | ||
69 | text = &tmp; | ||
70 | } | ||
71 | ast_from_text(&format!("fn f<{}>() {{ }}", text)) | ||
72 | } | ||
73 | |||
34 | // FIXME: replace stringly-typed constructor with a family of typed ctors, a-la | 74 | // FIXME: replace stringly-typed constructor with a family of typed ctors, a-la |
35 | // `expr_xxx`. | 75 | // `expr_xxx`. |
36 | pub fn ty(text: &str) -> ast::Type { | 76 | pub fn ty(text: &str) -> ast::Type { |
37 | ast_from_text(&format!("fn f() -> {} {{}}", text)) | 77 | ty_from_text(text) |
38 | } | 78 | } |
39 | pub fn ty_unit() -> ast::Type { | 79 | pub fn ty_unit() -> ast::Type { |
40 | ty("()") | 80 | ty_from_text("()") |
41 | } | 81 | } |
42 | pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type { | 82 | pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type { |
43 | let mut count: usize = 0; | 83 | let mut count: usize = 0; |
@@ -46,15 +86,16 @@ pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type { | |||
46 | contents.push(','); | 86 | contents.push(','); |
47 | } | 87 | } |
48 | 88 | ||
49 | ty(&format!("({})", contents)) | 89 | ty_from_text(&format!("({})", contents)) |
50 | } | ||
51 | // FIXME: handle path to type | ||
52 | pub fn ty_generic(name: ast::NameRef, types: impl IntoIterator<Item = ast::Type>) -> ast::Type { | ||
53 | let contents = types.into_iter().join(", "); | ||
54 | ty(&format!("{}<{}>", name, contents)) | ||
55 | } | 90 | } |
56 | pub fn ty_ref(target: ast::Type, exclusive: bool) -> ast::Type { | 91 | pub fn ty_ref(target: ast::Type, exclusive: bool) -> ast::Type { |
57 | ty(&if exclusive { format!("&mut {}", target) } else { format!("&{}", target) }) | 92 | ty_from_text(&if exclusive { format!("&mut {}", target) } else { format!("&{}", target) }) |
93 | } | ||
94 | pub fn ty_path(path: ast::Path) -> ast::Type { | ||
95 | ty_from_text(&path.to_string()) | ||
96 | } | ||
97 | fn ty_from_text(text: &str) -> ast::Type { | ||
98 | ast_from_text(&format!("type _T = {};", text)) | ||
58 | } | 99 | } |
59 | 100 | ||
60 | pub fn assoc_item_list() -> ast::AssocItemList { | 101 | pub fn assoc_item_list() -> ast::AssocItemList { |
@@ -88,7 +129,7 @@ pub fn path_unqualified(segment: ast::PathSegment) -> ast::Path { | |||
88 | pub fn path_qualified(qual: ast::Path, segment: ast::PathSegment) -> ast::Path { | 129 | pub fn path_qualified(qual: ast::Path, segment: ast::PathSegment) -> ast::Path { |
89 | ast_from_text(&format!("{}::{}", qual, segment)) | 130 | ast_from_text(&format!("{}::{}", qual, segment)) |
90 | } | 131 | } |
91 | 132 | // FIXME: path concatenation operation doesn't make sense as AST op. | |
92 | pub fn path_concat(first: ast::Path, second: ast::Path) -> ast::Path { | 133 | pub fn path_concat(first: ast::Path, second: ast::Path) -> ast::Path { |
93 | ast_from_text(&format!("{}::{}", first, second)) | 134 | ast_from_text(&format!("{}::{}", first, second)) |
94 | } | 135 | } |
@@ -104,15 +145,14 @@ pub fn path_from_segments( | |||
104 | format!("use {};", segments) | 145 | format!("use {};", segments) |
105 | }) | 146 | }) |
106 | } | 147 | } |
107 | 148 | // FIXME: should not be pub | |
108 | pub fn path_from_text(text: &str) -> ast::Path { | 149 | pub fn path_from_text(text: &str) -> ast::Path { |
109 | ast_from_text(&format!("fn main() {{ let test = {}; }}", text)) | 150 | ast_from_text(&format!("fn main() {{ let test = {}; }}", text)) |
110 | } | 151 | } |
111 | 152 | ||
112 | pub fn glob_use_tree() -> ast::UseTree { | 153 | pub fn use_tree_glob() -> ast::UseTree { |
113 | ast_from_text("use *;") | 154 | ast_from_text("use *;") |
114 | } | 155 | } |
115 | |||
116 | pub fn use_tree( | 156 | pub fn use_tree( |
117 | path: ast::Path, | 157 | path: ast::Path, |
118 | use_tree_list: Option<ast::UseTreeList>, | 158 | use_tree_list: Option<ast::UseTreeList>, |
@@ -207,15 +247,6 @@ pub fn expr_literal(text: &str) -> ast::Literal { | |||
207 | pub fn expr_empty_block() -> ast::Expr { | 247 | pub fn expr_empty_block() -> ast::Expr { |
208 | expr_from_text("{}") | 248 | expr_from_text("{}") |
209 | } | 249 | } |
210 | pub fn expr_unimplemented() -> ast::Expr { | ||
211 | expr_from_text("unimplemented!()") | ||
212 | } | ||
213 | pub fn expr_unreachable() -> ast::Expr { | ||
214 | expr_from_text("unreachable!()") | ||
215 | } | ||
216 | pub fn expr_todo() -> ast::Expr { | ||
217 | expr_from_text("todo!()") | ||
218 | } | ||
219 | pub fn expr_path(path: ast::Path) -> ast::Expr { | 250 | pub fn expr_path(path: ast::Path) -> ast::Expr { |
220 | expr_from_text(&path.to_string()) | 251 | expr_from_text(&path.to_string()) |
221 | } | 252 | } |
@@ -444,17 +475,6 @@ pub fn expr_stmt(expr: ast::Expr) -> ast::ExprStmt { | |||
444 | ast_from_text(&format!("fn f() {{ {}{} (); }}", expr, semi)) | 475 | ast_from_text(&format!("fn f() {{ {}{} (); }}", expr, semi)) |
445 | } | 476 | } |
446 | 477 | ||
447 | pub fn token(kind: SyntaxKind) -> SyntaxToken { | ||
448 | tokens::SOURCE_FILE | ||
449 | .tree() | ||
450 | .syntax() | ||
451 | .clone_for_update() | ||
452 | .descendants_with_tokens() | ||
453 | .filter_map(|it| it.into_token()) | ||
454 | .find(|it| it.kind() == kind) | ||
455 | .unwrap_or_else(|| panic!("unhandled token: {:?}", kind)) | ||
456 | } | ||
457 | |||
458 | pub fn param(pat: ast::Pat, ty: ast::Type) -> ast::Param { | 478 | pub fn param(pat: ast::Pat, ty: ast::Type) -> ast::Param { |
459 | ast_from_text(&format!("fn f({}: {}) {{ }}", pat, ty)) | 479 | ast_from_text(&format!("fn f({}: {}) {{ }}", pat, ty)) |
460 | } | 480 | } |
@@ -476,7 +496,7 @@ pub fn param_list( | |||
476 | ast_from_text(&list) | 496 | ast_from_text(&list) |
477 | } | 497 | } |
478 | 498 | ||
479 | pub fn generic_param(name: String, ty: Option<ast::TypeBoundList>) -> ast::GenericParam { | 499 | pub fn type_param(name: ast::Name, ty: Option<ast::TypeBoundList>) -> ast::TypeParam { |
480 | let bound = match ty { | 500 | let bound = match ty { |
481 | Some(it) => format!(": {}", it), | 501 | Some(it) => format!(": {}", it), |
482 | None => String::new(), | 502 | None => String::new(), |
@@ -484,6 +504,10 @@ pub fn generic_param(name: String, ty: Option<ast::TypeBoundList>) -> ast::Gener | |||
484 | ast_from_text(&format!("fn f<{}{}>() {{ }}", name, bound)) | 504 | ast_from_text(&format!("fn f<{}{}>() {{ }}", name, bound)) |
485 | } | 505 | } |
486 | 506 | ||
507 | pub fn lifetime_param(lifetime: ast::Lifetime) -> ast::LifetimeParam { | ||
508 | ast_from_text(&format!("fn f<{}>() {{ }}", lifetime)) | ||
509 | } | ||
510 | |||
487 | pub fn generic_param_list( | 511 | pub fn generic_param_list( |
488 | pats: impl IntoIterator<Item = ast::GenericParam>, | 512 | pats: impl IntoIterator<Item = ast::GenericParam>, |
489 | ) -> ast::GenericParamList { | 513 | ) -> ast::GenericParamList { |
@@ -588,6 +612,17 @@ fn unroot(n: SyntaxNode) -> SyntaxNode { | |||
588 | SyntaxNode::new_root(n.green().into()) | 612 | SyntaxNode::new_root(n.green().into()) |
589 | } | 613 | } |
590 | 614 | ||
615 | pub fn token(kind: SyntaxKind) -> SyntaxToken { | ||
616 | tokens::SOURCE_FILE | ||
617 | .tree() | ||
618 | .syntax() | ||
619 | .clone_for_update() | ||
620 | .descendants_with_tokens() | ||
621 | .filter_map(|it| it.into_token()) | ||
622 | .find(|it| it.kind() == kind) | ||
623 | .unwrap_or_else(|| panic!("unhandled token: {:?}", kind)) | ||
624 | } | ||
625 | |||
591 | pub mod tokens { | 626 | pub mod tokens { |
592 | use once_cell::sync::Lazy; | 627 | use once_cell::sync::Lazy; |
593 | 628 | ||