diff options
-rw-r--r-- | Cargo.lock | 4 | ||||
-rw-r--r-- | crates/ide_db/src/helpers/merge_imports.rs | 34 | ||||
-rw-r--r-- | crates/syntax/Cargo.toml | 2 | ||||
-rw-r--r-- | crates/syntax/src/algo.rs | 35 | ||||
-rw-r--r-- | crates/syntax/src/ast.rs | 6 | ||||
-rw-r--r-- | crates/syntax/src/ast/make.rs | 10 | ||||
-rw-r--r-- | docs/dev/style.md | 22 |
7 files changed, 57 insertions, 56 deletions
diff --git a/Cargo.lock b/Cargo.lock index f9c34547e..a19b8339c 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -1295,9 +1295,9 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" | |||
1295 | 1295 | ||
1296 | [[package]] | 1296 | [[package]] |
1297 | name = "rowan" | 1297 | name = "rowan" |
1298 | version = "0.13.0-pre.5" | 1298 | version = "0.13.0-pre.6" |
1299 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1299 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1300 | checksum = "32a5fc82ed0b7e7fba157331f0d8f64abd73bced6e7ac2a4dfa0c4cf0ab584e8" | 1300 | checksum = "82ccc04e145e9a5ab51b9c12a81d77c4a8250d87a407ab02ac650451141ff00d" |
1301 | dependencies = [ | 1301 | dependencies = [ |
1302 | "countme", | 1302 | "countme", |
1303 | "hashbrown", | 1303 | "hashbrown", |
diff --git a/crates/ide_db/src/helpers/merge_imports.rs b/crates/ide_db/src/helpers/merge_imports.rs index af2a51a4d..475ef99b5 100644 --- a/crates/ide_db/src/helpers/merge_imports.rs +++ b/crates/ide_db/src/helpers/merge_imports.rs | |||
@@ -2,8 +2,9 @@ | |||
2 | use std::cmp::Ordering; | 2 | use std::cmp::Ordering; |
3 | 3 | ||
4 | use itertools::{EitherOrBoth, Itertools}; | 4 | use itertools::{EitherOrBoth, Itertools}; |
5 | use syntax::ast::{ | 5 | use syntax::{ |
6 | self, edit::AstNodeEdit, make, AstNode, AttrsOwner, PathSegmentKind, VisibilityOwner, | 6 | ast::{self, make, AstNode, AttrsOwner, PathSegmentKind, VisibilityOwner}, |
7 | ted, | ||
7 | }; | 8 | }; |
8 | 9 | ||
9 | /// What type of merges are allowed. | 10 | /// What type of merges are allowed. |
@@ -65,7 +66,7 @@ pub fn try_merge_trees( | |||
65 | } else { | 66 | } else { |
66 | (lhs.split_prefix(&lhs_prefix), rhs.split_prefix(&rhs_prefix)) | 67 | (lhs.split_prefix(&lhs_prefix), rhs.split_prefix(&rhs_prefix)) |
67 | }; | 68 | }; |
68 | recursive_merge(&lhs, &rhs, merge) | 69 | recursive_merge(&lhs, &rhs, merge).map(|it| it.clone_for_update()) |
69 | } | 70 | } |
70 | 71 | ||
71 | /// Recursively "zips" together lhs and rhs. | 72 | /// Recursively "zips" together lhs and rhs. |
@@ -78,7 +79,8 @@ fn recursive_merge( | |||
78 | .use_tree_list() | 79 | .use_tree_list() |
79 | .into_iter() | 80 | .into_iter() |
80 | .flat_map(|list| list.use_trees()) | 81 | .flat_map(|list| list.use_trees()) |
81 | // we use Option here to early return from this function(this is not the same as a `filter` op) | 82 | // We use Option here to early return from this function(this is not the |
83 | // same as a `filter` op). | ||
82 | .map(|tree| match merge.is_tree_allowed(&tree) { | 84 | .map(|tree| match merge.is_tree_allowed(&tree) { |
83 | true => Some(tree), | 85 | true => Some(tree), |
84 | false => None, | 86 | false => None, |
@@ -111,8 +113,10 @@ fn recursive_merge( | |||
111 | let tree_is_self = |tree: ast::UseTree| { | 113 | let tree_is_self = |tree: ast::UseTree| { |
112 | tree.path().as_ref().map(path_is_self).unwrap_or(false) | 114 | tree.path().as_ref().map(path_is_self).unwrap_or(false) |
113 | }; | 115 | }; |
114 | // check if only one of the two trees has a tree list, and whether that then contains `self` or not. | 116 | // Check if only one of the two trees has a tree list, and |
115 | // If this is the case we can skip this iteration since the path without the list is already included in the other one via `self` | 117 | // whether that then contains `self` or not. If this is the |
118 | // case we can skip this iteration since the path without | ||
119 | // the list is already included in the other one via `self`. | ||
116 | let tree_contains_self = |tree: &ast::UseTree| { | 120 | let tree_contains_self = |tree: &ast::UseTree| { |
117 | tree.use_tree_list() | 121 | tree.use_tree_list() |
118 | .map(|tree_list| tree_list.use_trees().any(tree_is_self)) | 122 | .map(|tree_list| tree_list.use_trees().any(tree_is_self)) |
@@ -127,9 +131,11 @@ fn recursive_merge( | |||
127 | _ => (), | 131 | _ => (), |
128 | } | 132 | } |
129 | 133 | ||
130 | // glob imports arent part of the use-tree lists so we need to special handle them here as well | 134 | // Glob imports aren't part of the use-tree lists so we need |
131 | // this special handling is only required for when we merge a module import into a glob import of said module | 135 | // to special handle them here as well this special handling |
132 | // see the `merge_self_glob` or `merge_mod_into_glob` tests | 136 | // is only required for when we merge a module import into a |
137 | // glob import of said module see the `merge_self_glob` or | ||
138 | // `merge_mod_into_glob` tests. | ||
133 | if lhs_t.star_token().is_some() || rhs_t.star_token().is_some() { | 139 | if lhs_t.star_token().is_some() || rhs_t.star_token().is_some() { |
134 | *lhs_t = make::use_tree( | 140 | *lhs_t = make::use_tree( |
135 | make::path_unqualified(make::path_segment_self()), | 141 | make::path_unqualified(make::path_segment_self()), |
@@ -165,11 +171,11 @@ fn recursive_merge( | |||
165 | } | 171 | } |
166 | } | 172 | } |
167 | 173 | ||
168 | Some(if let Some(old) = lhs.use_tree_list() { | 174 | let lhs = lhs.clone_subtree().clone_for_update(); |
169 | lhs.replace_descendant(old, make::use_tree_list(use_trees)).clone_for_update() | 175 | if let Some(old) = lhs.use_tree_list() { |
170 | } else { | 176 | ted::replace(old.syntax(), make::use_tree_list(use_trees).syntax().clone_for_update()); |
171 | lhs.clone() | 177 | } |
172 | }) | 178 | ast::UseTree::cast(lhs.syntax().clone_subtree()) |
173 | } | 179 | } |
174 | 180 | ||
175 | /// Traverses both paths until they differ, returning the common prefix of both. | 181 | /// Traverses both paths until they differ, returning the common prefix of both. |
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml index c0bc59918..747f0b9eb 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.5" | 16 | rowan = "=0.13.0-pre.6" |
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 3f9b84ab9..825ea3185 100644 --- a/crates/syntax/src/algo.rs +++ b/crates/syntax/src/algo.rs | |||
@@ -337,7 +337,7 @@ enum InsertPos { | |||
337 | } | 337 | } |
338 | 338 | ||
339 | #[derive(Default)] | 339 | #[derive(Default)] |
340 | pub struct SyntaxRewriter<'a> { | 340 | pub(crate) struct SyntaxRewriter<'a> { |
341 | //FIXME: add debug_assertions that all elements are in fact from the same file. | 341 | //FIXME: add debug_assertions that all elements are in fact from the same file. |
342 | replacements: FxHashMap<SyntaxElement, Replacement>, | 342 | replacements: FxHashMap<SyntaxElement, Replacement>, |
343 | insertions: IndexMap<InsertPos, Vec<SyntaxElement>>, | 343 | insertions: IndexMap<InsertPos, Vec<SyntaxElement>>, |
@@ -354,13 +354,13 @@ impl fmt::Debug for SyntaxRewriter<'_> { | |||
354 | } | 354 | } |
355 | 355 | ||
356 | impl SyntaxRewriter<'_> { | 356 | impl SyntaxRewriter<'_> { |
357 | pub fn replace<T: Clone + Into<SyntaxElement>>(&mut self, what: &T, with: &T) { | 357 | pub(crate) fn replace<T: Clone + Into<SyntaxElement>>(&mut self, what: &T, with: &T) { |
358 | let what = what.clone().into(); | 358 | let what = what.clone().into(); |
359 | let replacement = Replacement::Single(with.clone().into()); | 359 | let replacement = Replacement::Single(with.clone().into()); |
360 | self.replacements.insert(what, replacement); | 360 | self.replacements.insert(what, replacement); |
361 | } | 361 | } |
362 | 362 | ||
363 | pub fn rewrite(&self, node: &SyntaxNode) -> SyntaxNode { | 363 | pub(crate) fn rewrite(&self, node: &SyntaxNode) -> SyntaxNode { |
364 | let _p = profile::span("rewrite"); | 364 | let _p = profile::span("rewrite"); |
365 | 365 | ||
366 | if self.replacements.is_empty() && self.insertions.is_empty() { | 366 | if self.replacements.is_empty() && self.insertions.is_empty() { |
@@ -370,37 +370,10 @@ impl SyntaxRewriter<'_> { | |||
370 | with_green(node, green) | 370 | with_green(node, green) |
371 | } | 371 | } |
372 | 372 | ||
373 | pub fn rewrite_ast<N: AstNode>(self, node: &N) -> N { | 373 | pub(crate) fn rewrite_ast<N: AstNode>(self, node: &N) -> N { |
374 | N::cast(self.rewrite(node.syntax())).unwrap() | 374 | N::cast(self.rewrite(node.syntax())).unwrap() |
375 | } | 375 | } |
376 | 376 | ||
377 | /// Returns a node that encompasses all replacements to be done by this rewriter. | ||
378 | /// | ||
379 | /// Passing the returned node to `rewrite` will apply all replacements queued up in `self`. | ||
380 | /// | ||
381 | /// Returns `None` when there are no replacements. | ||
382 | pub fn rewrite_root(&self) -> Option<SyntaxNode> { | ||
383 | let _p = profile::span("rewrite_root"); | ||
384 | fn element_to_node_or_parent(element: &SyntaxElement) -> Option<SyntaxNode> { | ||
385 | match element { | ||
386 | SyntaxElement::Node(it) => Some(it.clone()), | ||
387 | SyntaxElement::Token(it) => it.parent(), | ||
388 | } | ||
389 | } | ||
390 | |||
391 | self.replacements | ||
392 | .keys() | ||
393 | .filter_map(element_to_node_or_parent) | ||
394 | .chain(self.insertions.keys().filter_map(|pos| match pos { | ||
395 | InsertPos::FirstChildOf(it) => Some(it.clone()), | ||
396 | InsertPos::After(it) => element_to_node_or_parent(it), | ||
397 | })) | ||
398 | // If we only have one replacement/insertion, we must return its parent node, since `rewrite` does | ||
399 | // not replace the node passed to it. | ||
400 | .map(|it| it.parent().unwrap_or(it)) | ||
401 | .fold1(|a, b| least_common_ancestor(&a, &b).unwrap()) | ||
402 | } | ||
403 | |||
404 | fn replacement(&self, element: &SyntaxElement) -> Option<Replacement> { | 377 | fn replacement(&self, element: &SyntaxElement) -> Option<Replacement> { |
405 | self.replacements.get(element).cloned() | 378 | self.replacements.get(element).cloned() |
406 | } | 379 | } |
diff --git a/crates/syntax/src/ast.rs b/crates/syntax/src/ast.rs index 7f472d4db..a8071b51d 100644 --- a/crates/syntax/src/ast.rs +++ b/crates/syntax/src/ast.rs | |||
@@ -47,6 +47,12 @@ pub trait AstNode { | |||
47 | { | 47 | { |
48 | Self::cast(self.syntax().clone_for_update()).unwrap() | 48 | Self::cast(self.syntax().clone_for_update()).unwrap() |
49 | } | 49 | } |
50 | fn clone_subtree(&self) -> Self | ||
51 | where | ||
52 | Self: Sized, | ||
53 | { | ||
54 | Self::cast(self.syntax().clone_subtree()).unwrap() | ||
55 | } | ||
50 | } | 56 | } |
51 | 57 | ||
52 | /// Like `AstNode`, but wraps tokens rather than interior nodes. | 58 | /// Like `AstNode`, but wraps tokens rather than interior nodes. |
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 1998ad1f6..de04c8620 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs | |||
@@ -12,7 +12,7 @@ | |||
12 | use itertools::Itertools; | 12 | use itertools::Itertools; |
13 | use stdx::{format_to, never}; | 13 | use stdx::{format_to, never}; |
14 | 14 | ||
15 | use crate::{ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, SyntaxToken}; | 15 | use crate::{ast, AstNode, SourceFile, SyntaxKind, SyntaxToken}; |
16 | 16 | ||
17 | /// While the parent module defines basic atomic "constructors", the `ext` | 17 | /// While the parent module defines basic atomic "constructors", the `ext` |
18 | /// module defines shortcuts for common things. | 18 | /// module defines shortcuts for common things. |
@@ -601,17 +601,11 @@ fn ast_from_text<N: AstNode>(text: &str) -> N { | |||
601 | panic!("Failed to make ast node `{}` from text {}", std::any::type_name::<N>(), text) | 601 | panic!("Failed to make ast node `{}` from text {}", std::any::type_name::<N>(), text) |
602 | } | 602 | } |
603 | }; | 603 | }; |
604 | let node = node.syntax().clone(); | 604 | let node = node.clone_subtree(); |
605 | let node = unroot(node); | ||
606 | let node = N::cast(node).unwrap(); | ||
607 | assert_eq!(node.syntax().text_range().start(), 0.into()); | 605 | assert_eq!(node.syntax().text_range().start(), 0.into()); |
608 | node | 606 | node |
609 | } | 607 | } |
610 | 608 | ||
611 | fn unroot(n: SyntaxNode) -> SyntaxNode { | ||
612 | SyntaxNode::new_root(n.green().into()) | ||
613 | } | ||
614 | |||
615 | pub fn token(kind: SyntaxKind) -> SyntaxToken { | 609 | pub fn token(kind: SyntaxKind) -> SyntaxToken { |
616 | tokens::SOURCE_FILE | 610 | tokens::SOURCE_FILE |
617 | .tree() | 611 | .tree() |
diff --git a/docs/dev/style.md b/docs/dev/style.md index 9b0ddec66..f22b69768 100644 --- a/docs/dev/style.md +++ b/docs/dev/style.md | |||
@@ -951,6 +951,28 @@ match p.current() { | |||
951 | 951 | ||
952 | ## Documentation | 952 | ## Documentation |
953 | 953 | ||
954 | Style inline code comments as proper sentences. | ||
955 | Start with a capital letter, end with a dot. | ||
956 | |||
957 | ```rust | ||
958 | // GOOD | ||
959 | |||
960 | // Only simple single segment paths are allowed. | ||
961 | MergeBehavior::Last => { | ||
962 | tree.use_tree_list().is_none() && tree.path().map(path_len) <= Some(1) | ||
963 | } | ||
964 | |||
965 | // BAD | ||
966 | |||
967 | // only simple single segment paths are allowed | ||
968 | MergeBehavior::Last => { | ||
969 | tree.use_tree_list().is_none() && tree.path().map(path_len) <= Some(1) | ||
970 | } | ||
971 | ``` | ||
972 | |||
973 | **Rationale:** writing a sentence (or maybe even a paragraph) rather just "a comment" creates a more appropriate frame of mind. | ||
974 | It tricks you into writing down more of the context you keep in your head while coding. | ||
975 | |||
954 | For `.md` and `.adoc` files, prefer a sentence-per-line format, don't wrap lines. | 976 | For `.md` and `.adoc` files, prefer a sentence-per-line format, don't wrap lines. |
955 | If the line is too long, you want to split the sentence in two :-) | 977 | If the line is too long, you want to split the sentence in two :-) |
956 | 978 | ||