diff options
-rw-r--r-- | crates/assists/src/utils/insert_use.rs | 96 |
1 files changed, 77 insertions, 19 deletions
diff --git a/crates/assists/src/utils/insert_use.rs b/crates/assists/src/utils/insert_use.rs index 84a0dffdd..af3fc96b6 100644 --- a/crates/assists/src/utils/insert_use.rs +++ b/crates/assists/src/utils/insert_use.rs | |||
@@ -9,7 +9,7 @@ use syntax::{ | |||
9 | edit::{AstNodeEdit, IndentLevel}, | 9 | edit::{AstNodeEdit, IndentLevel}, |
10 | make, AstNode, PathSegmentKind, VisibilityOwner, | 10 | make, AstNode, PathSegmentKind, VisibilityOwner, |
11 | }, | 11 | }, |
12 | InsertPosition, SyntaxElement, SyntaxNode, | 12 | AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken, |
13 | }; | 13 | }; |
14 | use test_utils::mark; | 14 | use test_utils::mark; |
15 | 15 | ||
@@ -63,27 +63,30 @@ impl ImportScope { | |||
63 | } | 63 | } |
64 | } | 64 | } |
65 | 65 | ||
66 | fn insert_pos_after_inner_attribute(&self) -> (InsertPosition<SyntaxElement>, AddBlankLine) { | 66 | fn insert_pos_after_last_inner_element(&self) -> (InsertPosition<SyntaxElement>, AddBlankLine) { |
67 | // check if the scope has inner attributes, we dont want to insert in front of them | 67 | self.as_syntax_node() |
68 | match self | 68 | .children_with_tokens() |
69 | .as_syntax_node() | 69 | .filter(|child| match child { |
70 | .children() | 70 | NodeOrToken::Node(node) => is_inner_attribute(node.clone()), |
71 | // no flat_map here cause we want to short circuit the iterator | 71 | NodeOrToken::Token(token) => is_inner_comment(token.clone()), |
72 | .map(ast::Attr::cast) | ||
73 | .take_while(|attr| { | ||
74 | attr.as_ref().map(|attr| attr.kind() == ast::AttrKind::Inner).unwrap_or(false) | ||
75 | }) | 72 | }) |
76 | .last() | 73 | .last() |
77 | .flatten() | 74 | .map(|last_inner_element| { |
78 | { | 75 | (InsertPosition::After(last_inner_element.into()), AddBlankLine::BeforeTwice) |
79 | Some(attr) => { | 76 | }) |
80 | (InsertPosition::After(attr.syntax().clone().into()), AddBlankLine::BeforeTwice) | 77 | .unwrap_or_else(|| self.first_insert_pos()) |
81 | } | ||
82 | None => self.first_insert_pos(), | ||
83 | } | ||
84 | } | 78 | } |
85 | } | 79 | } |
86 | 80 | ||
81 | fn is_inner_attribute(node: SyntaxNode) -> bool { | ||
82 | ast::Attr::cast(node).map(|attr| attr.kind()) == Some(ast::AttrKind::Inner) | ||
83 | } | ||
84 | |||
85 | fn is_inner_comment(token: SyntaxToken) -> bool { | ||
86 | ast::Comment::cast(token).and_then(|comment| comment.kind().doc) | ||
87 | == Some(ast::CommentPlacement::Inner) | ||
88 | } | ||
89 | |||
87 | /// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur. | 90 | /// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur. |
88 | pub(crate) fn insert_use<'a>( | 91 | pub(crate) fn insert_use<'a>( |
89 | scope: &ImportScope, | 92 | scope: &ImportScope, |
@@ -558,7 +561,7 @@ fn find_insert_position( | |||
558 | (InsertPosition::After(node.into()), AddBlankLine::BeforeTwice) | 561 | (InsertPosition::After(node.into()), AddBlankLine::BeforeTwice) |
559 | } | 562 | } |
560 | // there are no imports in this file at all | 563 | // there are no imports in this file at all |
561 | None => scope.insert_pos_after_inner_attribute(), | 564 | None => scope.insert_pos_after_last_inner_element(), |
562 | }, | 565 | }, |
563 | } | 566 | } |
564 | } | 567 | } |
@@ -830,12 +833,67 @@ use foo::bar;", | |||
830 | "foo::bar", | 833 | "foo::bar", |
831 | r"#![allow(unused_imports)] | 834 | r"#![allow(unused_imports)] |
832 | 835 | ||
836 | #![no_std] | ||
833 | fn main() {}", | 837 | fn main() {}", |
834 | r"#![allow(unused_imports)] | 838 | r"#![allow(unused_imports)] |
835 | 839 | ||
836 | use foo::bar; | 840 | #![no_std] |
837 | 841 | ||
842 | use foo::bar; | ||
838 | fn main() {}", | 843 | fn main() {}", |
844 | ); | ||
845 | } | ||
846 | |||
847 | #[test] | ||
848 | fn inserts_after_single_line_inner_comments() { | ||
849 | check_none( | ||
850 | "foo::bar::Baz", | ||
851 | "//! Single line inner comments do not allow any code before them.", | ||
852 | r#"//! Single line inner comments do not allow any code before them. | ||
853 | |||
854 | use foo::bar::Baz;"#, | ||
855 | ); | ||
856 | } | ||
857 | |||
858 | #[test] | ||
859 | fn inserts_after_multiline_inner_comments() { | ||
860 | check_none( | ||
861 | "foo::bar::Baz", | ||
862 | r#"/*! Multiline inner comments do not allow any code before them. */ | ||
863 | |||
864 | /*! Still an inner comment, cannot place any code before. */ | ||
865 | fn main() {}"#, | ||
866 | r#"/*! Multiline inner comments do not allow any code before them. */ | ||
867 | |||
868 | /*! Still an inner comment, cannot place any code before. */ | ||
869 | |||
870 | use foo::bar::Baz; | ||
871 | fn main() {}"#, | ||
872 | ) | ||
873 | } | ||
874 | |||
875 | #[test] | ||
876 | fn inserts_after_all_inner_items() { | ||
877 | check_none( | ||
878 | "foo::bar::Baz", | ||
879 | r#"#![allow(unused_imports)] | ||
880 | /*! Multiline line comment 2 */ | ||
881 | |||
882 | |||
883 | //! Single line comment 1 | ||
884 | #![no_std] | ||
885 | //! Single line comment 2 | ||
886 | fn main() {}"#, | ||
887 | r#"#![allow(unused_imports)] | ||
888 | /*! Multiline line comment 2 */ | ||
889 | |||
890 | |||
891 | //! Single line comment 1 | ||
892 | #![no_std] | ||
893 | //! Single line comment 2 | ||
894 | |||
895 | use foo::bar::Baz; | ||
896 | fn main() {}"#, | ||
839 | ) | 897 | ) |
840 | } | 898 | } |
841 | 899 | ||