aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorKirill Bulatov <[email protected]>2020-11-11 09:37:41 +0000
committerKirill Bulatov <[email protected]>2020-11-12 11:46:02 +0000
commit335edf87bc84bdfcc48ab23f12f606ced09dbac7 (patch)
tree4415c7ec1d8dc1ca2e98f2d5243031356236ea8d /crates
parent99fa139beab0c03583c676554c3d807525f602af (diff)
Do not insert imports before inner comments
Diffstat (limited to 'crates')
-rw-r--r--crates/assists/src/utils/insert_use.rs110
1 files changed, 92 insertions, 18 deletions
diff --git a/crates/assists/src/utils/insert_use.rs b/crates/assists/src/utils/insert_use.rs
index 84a0dffdd..c4de83f77 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};
14use test_utils::mark; 14use test_utils::mark;
15 15
@@ -63,27 +63,46 @@ impl ImportScope {
63 } 63 }
64 } 64 }
65 65
66 fn insert_pos_after_inner_attribute(&self) -> (InsertPosition<SyntaxElement>, AddBlankLine) { 66 fn insert_pos_after_inner_elements(&self) -> (InsertPosition<SyntaxElement>, AddBlankLine) {
67 // check if the scope has inner attributes, we dont want to insert in front of them 67 let mut last_inner_element = None;
68 match self 68
69 .as_syntax_node() 69 for maybe_inner_element in self.as_syntax_node().children_with_tokens() {
70 .children() 70 match maybe_inner_element {
71 // no flat_map here cause we want to short circuit the iterator 71 NodeOrToken::Node(maybe_inner_node) => {
72 .map(ast::Attr::cast) 72 if is_inner_node(maybe_inner_node.clone()) {
73 .take_while(|attr| { 73 last_inner_element = Some(NodeOrToken::Node(maybe_inner_node))
74 attr.as_ref().map(|attr| attr.kind() == ast::AttrKind::Inner).unwrap_or(false) 74 } else {
75 }) 75 if let Some(maybe_inner_token) = maybe_inner_node.first_token() {
76 .last() 76 if is_inner_token(maybe_inner_token.clone()) {
77 .flatten() 77 last_inner_element = Some(NodeOrToken::Token(maybe_inner_token))
78 { 78 }
79 Some(attr) => { 79 }
80 (InsertPosition::After(attr.syntax().clone().into()), AddBlankLine::BeforeTwice) 80 };
81 }
82 NodeOrToken::Token(maybe_inner_token) => {
83 if is_inner_token(maybe_inner_token.clone()) {
84 last_inner_element = Some(NodeOrToken::Token(maybe_inner_token))
85 }
86 }
81 } 87 }
88 }
89
90 match last_inner_element {
91 Some(element) => (InsertPosition::After(element.into()), AddBlankLine::BeforeTwice),
82 None => self.first_insert_pos(), 92 None => self.first_insert_pos(),
83 } 93 }
84 } 94 }
85} 95}
86 96
97fn is_inner_node(node: SyntaxNode) -> bool {
98 ast::Attr::cast(node).map(|attr| attr.kind()) == Some(ast::AttrKind::Inner)
99}
100
101fn is_inner_token(token: SyntaxToken) -> bool {
102 ast::Comment::cast(token).and_then(|comment| comment.kind().doc)
103 == Some(ast::CommentPlacement::Inner)
104}
105
87/// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur. 106/// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur.
88pub(crate) fn insert_use<'a>( 107pub(crate) fn insert_use<'a>(
89 scope: &ImportScope, 108 scope: &ImportScope,
@@ -558,7 +577,7 @@ fn find_insert_position(
558 (InsertPosition::After(node.into()), AddBlankLine::BeforeTwice) 577 (InsertPosition::After(node.into()), AddBlankLine::BeforeTwice)
559 } 578 }
560 // there are no imports in this file at all 579 // there are no imports in this file at all
561 None => scope.insert_pos_after_inner_attribute(), 580 None => scope.insert_pos_after_inner_elements(),
562 }, 581 },
563 } 582 }
564 } 583 }
@@ -830,12 +849,67 @@ use foo::bar;",
830 "foo::bar", 849 "foo::bar",
831 r"#![allow(unused_imports)] 850 r"#![allow(unused_imports)]
832 851
852#![no_std]
833fn main() {}", 853fn main() {}",
834 r"#![allow(unused_imports)] 854 r"#![allow(unused_imports)]
835 855
836use foo::bar; 856#![no_std]
837 857
858use foo::bar;
838fn main() {}", 859fn main() {}",
860 );
861 }
862
863 #[test]
864 fn inserts_after_single_line_inner_comments() {
865 check_none(
866 "foo::bar::Baz",
867 "//! Single line inner comments do not allow any code before them.",
868 r#"//! Single line inner comments do not allow any code before them.
869
870use foo::bar::Baz;"#,
871 );
872 }
873
874 #[test]
875 fn inserts_after_multiline_inner_comments() {
876 check_none(
877 "foo::bar::Baz",
878 r#"/*! Multiline inner comments do not allow any code before them. */
879
880/*! RA considers this inner comment belonging to the function, yet we still cannot place the code before it. */
881fn main() {}"#,
882 r#"/*! Multiline inner comments do not allow any code before them. */
883
884/*! RA considers this inner comment belonging to the function, yet we still cannot place the code before it. */
885
886use foo::bar::Baz;
887fn main() {}"#,
888 )
889 }
890
891 #[test]
892 fn inserts_after_all_inner_items() {
893 check_none(
894 "foo::bar::Baz",
895 r#"#![allow(unused_imports)]
896/*! Multiline line comment 2 */
897
898
899//! Single line comment 1
900#![no_std]
901//! Single line comment 2
902fn main() {}"#,
903 r#"#![allow(unused_imports)]
904/*! Multiline line comment 2 */
905
906
907//! Single line comment 1
908#![no_std]
909//! Single line comment 2
910
911use foo::bar::Baz;
912fn main() {}"#,
839 ) 913 )
840 } 914 }
841 915