Skip to content

Commit 335edf8

Browse files
Do not insert imports before inner comments
1 parent 99fa139 commit 335edf8

File tree

1 file changed

+92
-18
lines changed

1 file changed

+92
-18
lines changed

crates/assists/src/utils/insert_use.rs

Lines changed: 92 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use syntax::{
99
edit::{AstNodeEdit, IndentLevel},
1010
make, AstNode, PathSegmentKind, VisibilityOwner,
1111
},
12-
InsertPosition, SyntaxElement, SyntaxNode,
12+
AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken,
1313
};
1414
use test_utils::mark;
1515

@@ -63,27 +63,46 @@ impl ImportScope {
6363
}
6464
}
6565

66-
fn insert_pos_after_inner_attribute(&self) -> (InsertPosition<SyntaxElement>, AddBlankLine) {
67-
// check if the scope has inner attributes, we dont want to insert in front of them
68-
match self
69-
.as_syntax_node()
70-
.children()
71-
// no flat_map here cause we want to short circuit the iterator
72-
.map(ast::Attr::cast)
73-
.take_while(|attr| {
74-
attr.as_ref().map(|attr| attr.kind() == ast::AttrKind::Inner).unwrap_or(false)
75-
})
76-
.last()
77-
.flatten()
78-
{
79-
Some(attr) => {
80-
(InsertPosition::After(attr.syntax().clone().into()), AddBlankLine::BeforeTwice)
66+
fn insert_pos_after_inner_elements(&self) -> (InsertPosition<SyntaxElement>, AddBlankLine) {
67+
let mut last_inner_element = None;
68+
69+
for maybe_inner_element in self.as_syntax_node().children_with_tokens() {
70+
match maybe_inner_element {
71+
NodeOrToken::Node(maybe_inner_node) => {
72+
if is_inner_node(maybe_inner_node.clone()) {
73+
last_inner_element = Some(NodeOrToken::Node(maybe_inner_node))
74+
} else {
75+
if let Some(maybe_inner_token) = maybe_inner_node.first_token() {
76+
if is_inner_token(maybe_inner_token.clone()) {
77+
last_inner_element = Some(NodeOrToken::Token(maybe_inner_token))
78+
}
79+
}
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+
}
8187
}
88+
}
89+
90+
match last_inner_element {
91+
Some(element) => (InsertPosition::After(element.into()), AddBlankLine::BeforeTwice),
8292
None => self.first_insert_pos(),
8393
}
8494
}
8595
}
8696

97+
fn is_inner_node(node: SyntaxNode) -> bool {
98+
ast::Attr::cast(node).map(|attr| attr.kind()) == Some(ast::AttrKind::Inner)
99+
}
100+
101+
fn is_inner_token(token: SyntaxToken) -> bool {
102+
ast::Comment::cast(token).and_then(|comment| comment.kind().doc)
103+
== Some(ast::CommentPlacement::Inner)
104+
}
105+
87106
/// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur.
88107
pub(crate) fn insert_use<'a>(
89108
scope: &ImportScope,
@@ -558,7 +577,7 @@ fn find_insert_position(
558577
(InsertPosition::After(node.into()), AddBlankLine::BeforeTwice)
559578
}
560579
// 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(),
562581
},
563582
}
564583
}
@@ -830,12 +849,67 @@ use foo::bar;",
830849
"foo::bar",
831850
r"#![allow(unused_imports)]
832851
852+
#![no_std]
833853
fn main() {}",
834854
r"#![allow(unused_imports)]
835855
836-
use foo::bar;
856+
#![no_std]
837857
858+
use foo::bar;
838859
fn 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+
870+
use 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. */
881+
fn 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+
886+
use foo::bar::Baz;
887+
fn 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
902+
fn 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+
911+
use foo::bar::Baz;
912+
fn main() {}"#,
839913
)
840914
}
841915

0 commit comments

Comments
 (0)