diff options
Diffstat (limited to 'crates/syntax')
-rw-r--r-- | crates/syntax/Cargo.toml | 2 | ||||
-rw-r--r-- | crates/syntax/src/algo.rs | 124 | ||||
-rw-r--r-- | crates/syntax/src/ast/edit.rs | 12 | ||||
-rw-r--r-- | crates/syntax/src/ast/edit_in_place.rs | 19 | ||||
-rw-r--r-- | crates/syntax/src/ast/make.rs | 128 | ||||
-rw-r--r-- | crates/syntax/src/ast/node_ext.rs | 25 | ||||
-rw-r--r-- | crates/syntax/src/ted.rs | 5 | ||||
-rw-r--r-- | crates/syntax/src/token_text.rs | 50 |
8 files changed, 152 insertions, 213 deletions
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml index 556f80882..c0bc59918 100644 --- a/crates/syntax/Cargo.toml +++ b/crates/syntax/Cargo.toml | |||
@@ -13,7 +13,7 @@ doctest = false | |||
13 | [dependencies] | 13 | [dependencies] |
14 | cov-mark = { version = "1.1", features = ["thread-local"] } | 14 | cov-mark = { version = "1.1", features = ["thread-local"] } |
15 | itertools = "0.10.0" | 15 | itertools = "0.10.0" |
16 | rowan = "=0.13.0-pre.3" | 16 | rowan = "=0.13.0-pre.5" |
17 | rustc_lexer = { version = "716.0.0", package = "rustc-ap-rustc_lexer" } | 17 | rustc_lexer = { version = "716.0.0", package = "rustc-ap-rustc_lexer" } |
18 | rustc-hash = "1.1.0" | 18 | rustc-hash = "1.1.0" |
19 | arrayvec = "0.7" | 19 | arrayvec = "0.7" |
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs index c9229c4e0..3f9b84ab9 100644 --- a/crates/syntax/src/algo.rs +++ b/crates/syntax/src/algo.rs | |||
@@ -1,10 +1,6 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use std::{ | 3 | use std::{fmt, hash::BuildHasherDefault, ops::RangeInclusive}; |
4 | fmt, | ||
5 | hash::BuildHasherDefault, | ||
6 | ops::{self, RangeInclusive}, | ||
7 | }; | ||
8 | 4 | ||
9 | use indexmap::IndexMap; | 5 | use indexmap::IndexMap; |
10 | use itertools::Itertools; | 6 | use itertools::Itertools; |
@@ -358,107 +354,11 @@ impl fmt::Debug for SyntaxRewriter<'_> { | |||
358 | } | 354 | } |
359 | 355 | ||
360 | impl SyntaxRewriter<'_> { | 356 | impl SyntaxRewriter<'_> { |
361 | pub fn delete<T: Clone + Into<SyntaxElement>>(&mut self, what: &T) { | ||
362 | let what = what.clone().into(); | ||
363 | let replacement = Replacement::Delete; | ||
364 | self.replacements.insert(what, replacement); | ||
365 | } | ||
366 | pub fn insert_before<T: Clone + Into<SyntaxElement>, U: Clone + Into<SyntaxElement>>( | ||
367 | &mut self, | ||
368 | before: &T, | ||
369 | what: &U, | ||
370 | ) { | ||
371 | let before = before.clone().into(); | ||
372 | let pos = match before.prev_sibling_or_token() { | ||
373 | Some(sibling) => InsertPos::After(sibling), | ||
374 | None => match before.parent() { | ||
375 | Some(parent) => InsertPos::FirstChildOf(parent), | ||
376 | None => return, | ||
377 | }, | ||
378 | }; | ||
379 | self.insertions.entry(pos).or_insert_with(Vec::new).push(what.clone().into()); | ||
380 | } | ||
381 | pub fn insert_after<T: Clone + Into<SyntaxElement>, U: Clone + Into<SyntaxElement>>( | ||
382 | &mut self, | ||
383 | after: &T, | ||
384 | what: &U, | ||
385 | ) { | ||
386 | self.insertions | ||
387 | .entry(InsertPos::After(after.clone().into())) | ||
388 | .or_insert_with(Vec::new) | ||
389 | .push(what.clone().into()); | ||
390 | } | ||
391 | pub fn insert_as_first_child<T: Clone + Into<SyntaxNode>, U: Clone + Into<SyntaxElement>>( | ||
392 | &mut self, | ||
393 | parent: &T, | ||
394 | what: &U, | ||
395 | ) { | ||
396 | self.insertions | ||
397 | .entry(InsertPos::FirstChildOf(parent.clone().into())) | ||
398 | .or_insert_with(Vec::new) | ||
399 | .push(what.clone().into()); | ||
400 | } | ||
401 | pub fn insert_many_before< | ||
402 | T: Clone + Into<SyntaxElement>, | ||
403 | U: IntoIterator<Item = SyntaxElement>, | ||
404 | >( | ||
405 | &mut self, | ||
406 | before: &T, | ||
407 | what: U, | ||
408 | ) { | ||
409 | let before = before.clone().into(); | ||
410 | let pos = match before.prev_sibling_or_token() { | ||
411 | Some(sibling) => InsertPos::After(sibling), | ||
412 | None => match before.parent() { | ||
413 | Some(parent) => InsertPos::FirstChildOf(parent), | ||
414 | None => return, | ||
415 | }, | ||
416 | }; | ||
417 | self.insertions.entry(pos).or_insert_with(Vec::new).extend(what); | ||
418 | } | ||
419 | pub fn insert_many_after< | ||
420 | T: Clone + Into<SyntaxElement>, | ||
421 | U: IntoIterator<Item = SyntaxElement>, | ||
422 | >( | ||
423 | &mut self, | ||
424 | after: &T, | ||
425 | what: U, | ||
426 | ) { | ||
427 | self.insertions | ||
428 | .entry(InsertPos::After(after.clone().into())) | ||
429 | .or_insert_with(Vec::new) | ||
430 | .extend(what); | ||
431 | } | ||
432 | pub fn insert_many_as_first_children< | ||
433 | T: Clone + Into<SyntaxNode>, | ||
434 | U: IntoIterator<Item = SyntaxElement>, | ||
435 | >( | ||
436 | &mut self, | ||
437 | parent: &T, | ||
438 | what: U, | ||
439 | ) { | ||
440 | self.insertions | ||
441 | .entry(InsertPos::FirstChildOf(parent.clone().into())) | ||
442 | .or_insert_with(Vec::new) | ||
443 | .extend(what) | ||
444 | } | ||
445 | pub fn replace<T: Clone + Into<SyntaxElement>>(&mut self, what: &T, with: &T) { | 357 | pub fn replace<T: Clone + Into<SyntaxElement>>(&mut self, what: &T, with: &T) { |
446 | let what = what.clone().into(); | 358 | let what = what.clone().into(); |
447 | let replacement = Replacement::Single(with.clone().into()); | 359 | let replacement = Replacement::Single(with.clone().into()); |
448 | self.replacements.insert(what, replacement); | 360 | self.replacements.insert(what, replacement); |
449 | } | 361 | } |
450 | pub fn replace_with_many<T: Clone + Into<SyntaxElement>>( | ||
451 | &mut self, | ||
452 | what: &T, | ||
453 | with: Vec<SyntaxElement>, | ||
454 | ) { | ||
455 | let what = what.clone().into(); | ||
456 | let replacement = Replacement::Many(with); | ||
457 | self.replacements.insert(what, replacement); | ||
458 | } | ||
459 | pub fn replace_ast<T: AstNode>(&mut self, what: &T, with: &T) { | ||
460 | self.replace(what.syntax(), with.syntax()) | ||
461 | } | ||
462 | 362 | ||
463 | pub fn rewrite(&self, node: &SyntaxNode) -> SyntaxNode { | 363 | pub fn rewrite(&self, node: &SyntaxNode) -> SyntaxNode { |
464 | let _p = profile::span("rewrite"); | 364 | let _p = profile::span("rewrite"); |
@@ -534,10 +434,6 @@ impl SyntaxRewriter<'_> { | |||
534 | if let Some(replacement) = self.replacement(&element) { | 434 | if let Some(replacement) = self.replacement(&element) { |
535 | match replacement { | 435 | match replacement { |
536 | Replacement::Single(element) => acc.push(element_to_green(element)), | 436 | Replacement::Single(element) => acc.push(element_to_green(element)), |
537 | Replacement::Many(replacements) => { | ||
538 | acc.extend(replacements.into_iter().map(element_to_green)) | ||
539 | } | ||
540 | Replacement::Delete => (), | ||
541 | }; | 437 | }; |
542 | } else { | 438 | } else { |
543 | match element { | 439 | match element { |
@@ -555,30 +451,14 @@ impl SyntaxRewriter<'_> { | |||
555 | 451 | ||
556 | fn element_to_green(element: SyntaxElement) -> NodeOrToken<rowan::GreenNode, rowan::GreenToken> { | 452 | fn element_to_green(element: SyntaxElement) -> NodeOrToken<rowan::GreenNode, rowan::GreenToken> { |
557 | match element { | 453 | match element { |
558 | NodeOrToken::Node(it) => NodeOrToken::Node(it.green()), | 454 | NodeOrToken::Node(it) => NodeOrToken::Node(it.green().into_owned()), |
559 | NodeOrToken::Token(it) => NodeOrToken::Token(it.green().to_owned()), | 455 | NodeOrToken::Token(it) => NodeOrToken::Token(it.green().to_owned()), |
560 | } | 456 | } |
561 | } | 457 | } |
562 | 458 | ||
563 | impl ops::AddAssign for SyntaxRewriter<'_> { | ||
564 | fn add_assign(&mut self, rhs: SyntaxRewriter) { | ||
565 | self.replacements.extend(rhs.replacements); | ||
566 | for (pos, insertions) in rhs.insertions.into_iter() { | ||
567 | match self.insertions.entry(pos) { | ||
568 | indexmap::map::Entry::Occupied(mut occupied) => { | ||
569 | occupied.get_mut().extend(insertions) | ||
570 | } | ||
571 | indexmap::map::Entry::Vacant(vacant) => drop(vacant.insert(insertions)), | ||
572 | } | ||
573 | } | ||
574 | } | ||
575 | } | ||
576 | |||
577 | #[derive(Clone, Debug)] | 459 | #[derive(Clone, Debug)] |
578 | enum Replacement { | 460 | enum Replacement { |
579 | Delete, | ||
580 | Single(SyntaxElement), | 461 | Single(SyntaxElement), |
581 | Many(Vec<SyntaxElement>), | ||
582 | } | 462 | } |
583 | 463 | ||
584 | fn with_children( | 464 | fn with_children( |
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs index 8c60927e4..cbc75f922 100644 --- a/crates/syntax/src/ast/edit.rs +++ b/crates/syntax/src/ast/edit.rs | |||
@@ -665,18 +665,8 @@ pub trait AstNodeEdit: AstNode + Clone + Sized { | |||
665 | 665 | ||
666 | #[must_use] | 666 | #[must_use] |
667 | fn replace_descendant<D: AstNode>(&self, old: D, new: D) -> Self { | 667 | fn replace_descendant<D: AstNode>(&self, old: D, new: D) -> Self { |
668 | self.replace_descendants(iter::once((old, new))) | ||
669 | } | ||
670 | |||
671 | #[must_use] | ||
672 | fn replace_descendants<D: AstNode>( | ||
673 | &self, | ||
674 | replacement_map: impl IntoIterator<Item = (D, D)>, | ||
675 | ) -> Self { | ||
676 | let mut rewriter = SyntaxRewriter::default(); | 668 | let mut rewriter = SyntaxRewriter::default(); |
677 | for (from, to) in replacement_map { | 669 | rewriter.replace(old.syntax(), new.syntax()); |
678 | rewriter.replace(from.syntax(), to.syntax()) | ||
679 | } | ||
680 | rewriter.rewrite_ast(self) | 670 | rewriter.rewrite_ast(self) |
681 | } | 671 | } |
682 | fn indent_level(&self) -> IndentLevel { | 672 | fn indent_level(&self) -> IndentLevel { |
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 42da09606..1998ad1f6 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs | |||
@@ -3,31 +3,81 @@ | |||
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 | pub fn name(text: &str) -> ast::Name { | 17 | /// While the parent module defines basic atomic "constructors", the `ext` |
18 | ast_from_text(&format!("mod {};", text)) | 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 | } | ||
19 | } | 47 | } |
20 | 48 | ||
49 | pub fn name(text: &str) -> ast::Name { | ||
50 | ast_from_text(&format!("mod {}{};", raw_ident_esc(text), text)) | ||
51 | } | ||
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() {{ {}; }}", text)) | 53 | ast_from_text(&format!("fn f() {{ {}{}; }}", raw_ident_esc(text), text)) |
23 | } | 54 | } |
55 | fn raw_ident_esc(ident: &str) -> &'static str { | ||
56 | let is_keyword = parser::SyntaxKind::from_keyword(ident).is_some(); | ||
57 | if is_keyword && !matches!(ident, "self" | "crate" | "super" | "Self") { | ||
58 | "r#" | ||
59 | } else { | ||
60 | "" | ||
61 | } | ||
62 | } | ||
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 | |||
24 | // 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 |
25 | // `expr_xxx`. | 75 | // `expr_xxx`. |
26 | pub fn ty(text: &str) -> ast::Type { | 76 | pub fn ty(text: &str) -> ast::Type { |
27 | ast_from_text(&format!("fn f() -> {} {{}}", text)) | 77 | ty_from_text(text) |
28 | } | 78 | } |
29 | pub fn ty_unit() -> ast::Type { | 79 | pub fn ty_unit() -> ast::Type { |
30 | ty("()") | 80 | ty_from_text("()") |
31 | } | 81 | } |
32 | 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 { |
33 | let mut count: usize = 0; | 83 | let mut count: usize = 0; |
@@ -36,15 +86,16 @@ pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type { | |||
36 | contents.push(','); | 86 | contents.push(','); |
37 | } | 87 | } |
38 | 88 | ||
39 | ty(&format!("({})", contents)) | 89 | ty_from_text(&format!("({})", contents)) |
40 | } | ||
41 | // FIXME: handle path to type | ||
42 | pub fn ty_generic(name: ast::NameRef, types: impl IntoIterator<Item = ast::Type>) -> ast::Type { | ||
43 | let contents = types.into_iter().join(", "); | ||
44 | ty(&format!("{}<{}>", name, contents)) | ||
45 | } | 90 | } |
46 | pub fn ty_ref(target: ast::Type, exclusive: bool) -> ast::Type { | 91 | pub fn ty_ref(target: ast::Type, exclusive: bool) -> ast::Type { |
47 | 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)) | ||
48 | } | 99 | } |
49 | 100 | ||
50 | pub fn assoc_item_list() -> ast::AssocItemList { | 101 | pub fn assoc_item_list() -> ast::AssocItemList { |
@@ -78,7 +129,7 @@ pub fn path_unqualified(segment: ast::PathSegment) -> ast::Path { | |||
78 | 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 { |
79 | ast_from_text(&format!("{}::{}", qual, segment)) | 130 | ast_from_text(&format!("{}::{}", qual, segment)) |
80 | } | 131 | } |
81 | 132 | // FIXME: path concatenation operation doesn't make sense as AST op. | |
82 | 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 { |
83 | ast_from_text(&format!("{}::{}", first, second)) | 134 | ast_from_text(&format!("{}::{}", first, second)) |
84 | } | 135 | } |
@@ -94,15 +145,14 @@ pub fn path_from_segments( | |||
94 | format!("use {};", segments) | 145 | format!("use {};", segments) |
95 | }) | 146 | }) |
96 | } | 147 | } |
97 | 148 | // FIXME: should not be pub | |
98 | pub fn path_from_text(text: &str) -> ast::Path { | 149 | pub fn path_from_text(text: &str) -> ast::Path { |
99 | ast_from_text(&format!("fn main() {{ let test = {}; }}", text)) | 150 | ast_from_text(&format!("fn main() {{ let test = {}; }}", text)) |
100 | } | 151 | } |
101 | 152 | ||
102 | pub fn glob_use_tree() -> ast::UseTree { | 153 | pub fn use_tree_glob() -> ast::UseTree { |
103 | ast_from_text("use *;") | 154 | ast_from_text("use *;") |
104 | } | 155 | } |
105 | |||
106 | pub fn use_tree( | 156 | pub fn use_tree( |
107 | path: ast::Path, | 157 | path: ast::Path, |
108 | use_tree_list: Option<ast::UseTreeList>, | 158 | use_tree_list: Option<ast::UseTreeList>, |
@@ -197,15 +247,6 @@ pub fn expr_literal(text: &str) -> ast::Literal { | |||
197 | pub fn expr_empty_block() -> ast::Expr { | 247 | pub fn expr_empty_block() -> ast::Expr { |
198 | expr_from_text("{}") | 248 | expr_from_text("{}") |
199 | } | 249 | } |
200 | pub fn expr_unimplemented() -> ast::Expr { | ||
201 | expr_from_text("unimplemented!()") | ||
202 | } | ||
203 | pub fn expr_unreachable() -> ast::Expr { | ||
204 | expr_from_text("unreachable!()") | ||
205 | } | ||
206 | pub fn expr_todo() -> ast::Expr { | ||
207 | expr_from_text("todo!()") | ||
208 | } | ||
209 | pub fn expr_path(path: ast::Path) -> ast::Expr { | 250 | pub fn expr_path(path: ast::Path) -> ast::Expr { |
210 | expr_from_text(&path.to_string()) | 251 | expr_from_text(&path.to_string()) |
211 | } | 252 | } |
@@ -265,6 +306,9 @@ pub fn expr_tuple(elements: impl IntoIterator<Item = ast::Expr>) -> ast::Expr { | |||
265 | let expr = elements.into_iter().format(", "); | 306 | let expr = elements.into_iter().format(", "); |
266 | expr_from_text(&format!("({})", expr)) | 307 | expr_from_text(&format!("({})", expr)) |
267 | } | 308 | } |
309 | pub fn expr_assignment(lhs: ast::Expr, rhs: ast::Expr) -> ast::Expr { | ||
310 | expr_from_text(&format!("{} = {}", lhs, rhs)) | ||
311 | } | ||
268 | fn expr_from_text(text: &str) -> ast::Expr { | 312 | fn expr_from_text(text: &str) -> ast::Expr { |
269 | ast_from_text(&format!("const C: () = {};", text)) | 313 | ast_from_text(&format!("const C: () = {};", text)) |
270 | } | 314 | } |
@@ -431,17 +475,6 @@ pub fn expr_stmt(expr: ast::Expr) -> ast::ExprStmt { | |||
431 | ast_from_text(&format!("fn f() {{ {}{} (); }}", expr, semi)) | 475 | ast_from_text(&format!("fn f() {{ {}{} (); }}", expr, semi)) |
432 | } | 476 | } |
433 | 477 | ||
434 | pub fn token(kind: SyntaxKind) -> SyntaxToken { | ||
435 | tokens::SOURCE_FILE | ||
436 | .tree() | ||
437 | .syntax() | ||
438 | .clone_for_update() | ||
439 | .descendants_with_tokens() | ||
440 | .filter_map(|it| it.into_token()) | ||
441 | .find(|it| it.kind() == kind) | ||
442 | .unwrap_or_else(|| panic!("unhandled token: {:?}", kind)) | ||
443 | } | ||
444 | |||
445 | pub fn param(pat: ast::Pat, ty: ast::Type) -> ast::Param { | 478 | pub fn param(pat: ast::Pat, ty: ast::Type) -> ast::Param { |
446 | ast_from_text(&format!("fn f({}: {}) {{ }}", pat, ty)) | 479 | ast_from_text(&format!("fn f({}: {}) {{ }}", pat, ty)) |
447 | } | 480 | } |
@@ -463,7 +496,7 @@ pub fn param_list( | |||
463 | ast_from_text(&list) | 496 | ast_from_text(&list) |
464 | } | 497 | } |
465 | 498 | ||
466 | 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 { |
467 | let bound = match ty { | 500 | let bound = match ty { |
468 | Some(it) => format!(": {}", it), | 501 | Some(it) => format!(": {}", it), |
469 | None => String::new(), | 502 | None => String::new(), |
@@ -471,6 +504,10 @@ pub fn generic_param(name: String, ty: Option<ast::TypeBoundList>) -> ast::Gener | |||
471 | ast_from_text(&format!("fn f<{}{}>() {{ }}", name, bound)) | 504 | ast_from_text(&format!("fn f<{}{}>() {{ }}", name, bound)) |
472 | } | 505 | } |
473 | 506 | ||
507 | pub fn lifetime_param(lifetime: ast::Lifetime) -> ast::LifetimeParam { | ||
508 | ast_from_text(&format!("fn f<{}>() {{ }}", lifetime)) | ||
509 | } | ||
510 | |||
474 | pub fn generic_param_list( | 511 | pub fn generic_param_list( |
475 | pats: impl IntoIterator<Item = ast::GenericParam>, | 512 | pats: impl IntoIterator<Item = ast::GenericParam>, |
476 | ) -> ast::GenericParamList { | 513 | ) -> ast::GenericParamList { |
@@ -572,7 +609,18 @@ fn ast_from_text<N: AstNode>(text: &str) -> N { | |||
572 | } | 609 | } |
573 | 610 | ||
574 | fn unroot(n: SyntaxNode) -> SyntaxNode { | 611 | fn unroot(n: SyntaxNode) -> SyntaxNode { |
575 | SyntaxNode::new_root(n.green()) | 612 | SyntaxNode::new_root(n.green().into()) |
613 | } | ||
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)) | ||
576 | } | 624 | } |
577 | 625 | ||
578 | pub mod tokens { | 626 | pub mod tokens { |
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index 492fbc4a0..bef49238f 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs | |||
@@ -1,30 +1,31 @@ | |||
1 | //! Various extension methods to ast Nodes, which are hard to code-generate. | 1 | //! Various extension methods to ast Nodes, which are hard to code-generate. |
2 | //! Extensions for various expressions live in a sibling `expr_extensions` module. | 2 | //! Extensions for various expressions live in a sibling `expr_extensions` module. |
3 | 3 | ||
4 | use std::{fmt, iter::successors}; | 4 | use std::{borrow::Cow, fmt, iter::successors}; |
5 | 5 | ||
6 | use itertools::Itertools; | 6 | use itertools::Itertools; |
7 | use parser::SyntaxKind; | 7 | use parser::SyntaxKind; |
8 | use rowan::{GreenNodeData, GreenTokenData}; | ||
8 | 9 | ||
9 | use crate::{ | 10 | use crate::{ |
10 | ast::{self, support, AstNode, AstToken, AttrsOwner, NameOwner, SyntaxNode}, | 11 | ast::{self, support, AstNode, AstToken, AttrsOwner, NameOwner, SyntaxNode}, |
11 | SmolStr, SyntaxElement, SyntaxToken, TokenText, T, | 12 | NodeOrToken, SmolStr, SyntaxElement, SyntaxToken, TokenText, T, |
12 | }; | 13 | }; |
13 | 14 | ||
14 | impl ast::Lifetime { | 15 | impl ast::Lifetime { |
15 | pub fn text(&self) -> TokenText { | 16 | pub fn text(&self) -> TokenText<'_> { |
16 | text_of_first_token(self.syntax()) | 17 | text_of_first_token(self.syntax()) |
17 | } | 18 | } |
18 | } | 19 | } |
19 | 20 | ||
20 | impl ast::Name { | 21 | impl ast::Name { |
21 | pub fn text(&self) -> TokenText { | 22 | pub fn text(&self) -> TokenText<'_> { |
22 | text_of_first_token(self.syntax()) | 23 | text_of_first_token(self.syntax()) |
23 | } | 24 | } |
24 | } | 25 | } |
25 | 26 | ||
26 | impl ast::NameRef { | 27 | impl ast::NameRef { |
27 | pub fn text(&self) -> TokenText { | 28 | pub fn text(&self) -> TokenText<'_> { |
28 | text_of_first_token(self.syntax()) | 29 | text_of_first_token(self.syntax()) |
29 | } | 30 | } |
30 | 31 | ||
@@ -33,11 +34,15 @@ impl ast::NameRef { | |||
33 | } | 34 | } |
34 | } | 35 | } |
35 | 36 | ||
36 | fn text_of_first_token(node: &SyntaxNode) -> TokenText { | 37 | fn text_of_first_token(node: &SyntaxNode) -> TokenText<'_> { |
37 | let first_token = | 38 | fn first_token(green_ref: &GreenNodeData) -> &GreenTokenData { |
38 | node.green().children().next().and_then(|it| it.into_token()).unwrap().to_owned(); | 39 | green_ref.children().next().and_then(NodeOrToken::into_token).unwrap() |
40 | } | ||
39 | 41 | ||
40 | TokenText(first_token) | 42 | match node.green() { |
43 | Cow::Borrowed(green_ref) => TokenText::borrowed(first_token(green_ref).text()), | ||
44 | Cow::Owned(green) => TokenText::owned(first_token(&green).to_owned()), | ||
45 | } | ||
41 | } | 46 | } |
42 | 47 | ||
43 | #[derive(Debug, PartialEq, Eq, Clone)] | 48 | #[derive(Debug, PartialEq, Eq, Clone)] |
@@ -412,7 +417,7 @@ impl fmt::Display for NameOrNameRef { | |||
412 | } | 417 | } |
413 | 418 | ||
414 | impl NameOrNameRef { | 419 | impl NameOrNameRef { |
415 | pub fn text(&self) -> TokenText { | 420 | pub fn text(&self) -> TokenText<'_> { |
416 | match self { | 421 | match self { |
417 | NameOrNameRef::Name(name) => name.text(), | 422 | NameOrNameRef::Name(name) => name.text(), |
418 | NameOrNameRef::NameRef(name_ref) => name_ref.text(), | 423 | NameOrNameRef::NameRef(name_ref) => name_ref.text(), |
diff --git a/crates/syntax/src/ted.rs b/crates/syntax/src/ted.rs index 91a06101f..a50c0dbca 100644 --- a/crates/syntax/src/ted.rs +++ b/crates/syntax/src/ted.rs | |||
@@ -125,8 +125,11 @@ pub fn remove_all_iter(range: impl IntoIterator<Item = SyntaxElement>) { | |||
125 | } | 125 | } |
126 | 126 | ||
127 | pub fn replace(old: impl Element, new: impl Element) { | 127 | pub fn replace(old: impl Element, new: impl Element) { |
128 | replace_with_many(old, vec![new.syntax_element()]) | ||
129 | } | ||
130 | pub fn replace_with_many(old: impl Element, new: Vec<SyntaxElement>) { | ||
128 | let old = old.syntax_element(); | 131 | let old = old.syntax_element(); |
129 | replace_all(old.clone()..=old, vec![new.syntax_element()]) | 132 | replace_all(old.clone()..=old, new) |
130 | } | 133 | } |
131 | pub fn replace_all(range: RangeInclusive<SyntaxElement>, new: Vec<SyntaxElement>) { | 134 | pub fn replace_all(range: RangeInclusive<SyntaxElement>, new: Vec<SyntaxElement>) { |
132 | let start = range.start().index(); | 135 | let start = range.start().index(); |
diff --git a/crates/syntax/src/token_text.rs b/crates/syntax/src/token_text.rs index d2ed0a12a..f3e8b321a 100644 --- a/crates/syntax/src/token_text.rs +++ b/crates/syntax/src/token_text.rs | |||
@@ -2,75 +2,93 @@ | |||
2 | 2 | ||
3 | use std::{cmp::Ordering, fmt, ops}; | 3 | use std::{cmp::Ordering, fmt, ops}; |
4 | 4 | ||
5 | pub struct TokenText(pub(crate) rowan::GreenToken); | 5 | use rowan::GreenToken; |
6 | |||
7 | pub struct TokenText<'a>(pub(crate) Repr<'a>); | ||
8 | |||
9 | pub(crate) enum Repr<'a> { | ||
10 | Borrowed(&'a str), | ||
11 | Owned(GreenToken), | ||
12 | } | ||
13 | |||
14 | impl<'a> TokenText<'a> { | ||
15 | pub(crate) fn borrowed(text: &'a str) -> Self { | ||
16 | TokenText(Repr::Borrowed(text)) | ||
17 | } | ||
18 | |||
19 | pub(crate) fn owned(green: GreenToken) -> Self { | ||
20 | TokenText(Repr::Owned(green)) | ||
21 | } | ||
6 | 22 | ||
7 | impl TokenText { | ||
8 | pub fn as_str(&self) -> &str { | 23 | pub fn as_str(&self) -> &str { |
9 | self.0.text() | 24 | match self.0 { |
25 | Repr::Borrowed(it) => it, | ||
26 | Repr::Owned(ref green) => green.text(), | ||
27 | } | ||
10 | } | 28 | } |
11 | } | 29 | } |
12 | 30 | ||
13 | impl ops::Deref for TokenText { | 31 | impl ops::Deref for TokenText<'_> { |
14 | type Target = str; | 32 | type Target = str; |
15 | 33 | ||
16 | fn deref(&self) -> &str { | 34 | fn deref(&self) -> &str { |
17 | self.as_str() | 35 | self.as_str() |
18 | } | 36 | } |
19 | } | 37 | } |
20 | impl AsRef<str> for TokenText { | 38 | impl AsRef<str> for TokenText<'_> { |
21 | fn as_ref(&self) -> &str { | 39 | fn as_ref(&self) -> &str { |
22 | self.as_str() | 40 | self.as_str() |
23 | } | 41 | } |
24 | } | 42 | } |
25 | 43 | ||
26 | impl From<TokenText> for String { | 44 | impl From<TokenText<'_>> for String { |
27 | fn from(token_text: TokenText) -> Self { | 45 | fn from(token_text: TokenText) -> Self { |
28 | token_text.as_str().into() | 46 | token_text.as_str().into() |
29 | } | 47 | } |
30 | } | 48 | } |
31 | 49 | ||
32 | impl PartialEq<&'_ str> for TokenText { | 50 | impl PartialEq<&'_ str> for TokenText<'_> { |
33 | fn eq(&self, other: &&str) -> bool { | 51 | fn eq(&self, other: &&str) -> bool { |
34 | self.as_str() == *other | 52 | self.as_str() == *other |
35 | } | 53 | } |
36 | } | 54 | } |
37 | impl PartialEq<TokenText> for &'_ str { | 55 | impl PartialEq<TokenText<'_>> for &'_ str { |
38 | fn eq(&self, other: &TokenText) -> bool { | 56 | fn eq(&self, other: &TokenText) -> bool { |
39 | other == self | 57 | other == self |
40 | } | 58 | } |
41 | } | 59 | } |
42 | impl PartialEq<String> for TokenText { | 60 | impl PartialEq<String> for TokenText<'_> { |
43 | fn eq(&self, other: &String) -> bool { | 61 | fn eq(&self, other: &String) -> bool { |
44 | self.as_str() == other.as_str() | 62 | self.as_str() == other.as_str() |
45 | } | 63 | } |
46 | } | 64 | } |
47 | impl PartialEq<TokenText> for String { | 65 | impl PartialEq<TokenText<'_>> for String { |
48 | fn eq(&self, other: &TokenText) -> bool { | 66 | fn eq(&self, other: &TokenText) -> bool { |
49 | other == self | 67 | other == self |
50 | } | 68 | } |
51 | } | 69 | } |
52 | impl PartialEq for TokenText { | 70 | impl PartialEq for TokenText<'_> { |
53 | fn eq(&self, other: &TokenText) -> bool { | 71 | fn eq(&self, other: &TokenText) -> bool { |
54 | self.as_str() == other.as_str() | 72 | self.as_str() == other.as_str() |
55 | } | 73 | } |
56 | } | 74 | } |
57 | impl Eq for TokenText {} | 75 | impl Eq for TokenText<'_> {} |
58 | impl Ord for TokenText { | 76 | impl Ord for TokenText<'_> { |
59 | fn cmp(&self, other: &Self) -> Ordering { | 77 | fn cmp(&self, other: &Self) -> Ordering { |
60 | self.as_str().cmp(other.as_str()) | 78 | self.as_str().cmp(other.as_str()) |
61 | } | 79 | } |
62 | } | 80 | } |
63 | impl PartialOrd for TokenText { | 81 | impl PartialOrd for TokenText<'_> { |
64 | fn partial_cmp(&self, other: &Self) -> Option<Ordering> { | 82 | fn partial_cmp(&self, other: &Self) -> Option<Ordering> { |
65 | Some(self.cmp(other)) | 83 | Some(self.cmp(other)) |
66 | } | 84 | } |
67 | } | 85 | } |
68 | impl fmt::Display for TokenText { | 86 | impl fmt::Display for TokenText<'_> { |
69 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 87 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
70 | fmt::Display::fmt(self.as_str(), f) | 88 | fmt::Display::fmt(self.as_str(), f) |
71 | } | 89 | } |
72 | } | 90 | } |
73 | impl fmt::Debug for TokenText { | 91 | impl fmt::Debug for TokenText<'_> { |
74 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 92 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
75 | fmt::Debug::fmt(self.as_str(), f) | 93 | fmt::Debug::fmt(self.as_str(), f) |
76 | } | 94 | } |