aboutsummaryrefslogtreecommitdiff
path: root/crates/assists/src/utils/insert_use.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/assists/src/utils/insert_use.rs')
-rw-r--r--crates/assists/src/utils/insert_use.rs112
1 files changed, 86 insertions, 26 deletions
diff --git a/crates/assists/src/utils/insert_use.rs b/crates/assists/src/utils/insert_use.rs
index 84a0dffdd..423782a0e 100644
--- a/crates/assists/src/utils/insert_use.rs
+++ b/crates/assists/src/utils/insert_use.rs
@@ -1,6 +1,8 @@
1//! Handle syntactic aspects of inserting a new `use`. 1//! Handle syntactic aspects of inserting a new `use`.
2use std::{cmp::Ordering, iter::successors}; 2use std::{cmp::Ordering, iter::successors};
3 3
4use hir::Semantics;
5use ide_db::RootDatabase;
4use itertools::{EitherOrBoth, Itertools}; 6use itertools::{EitherOrBoth, Itertools};
5use syntax::{ 7use syntax::{
6 algo::SyntaxRewriter, 8 algo::SyntaxRewriter,
@@ -9,12 +11,12 @@ use syntax::{
9 edit::{AstNodeEdit, IndentLevel}, 11 edit::{AstNodeEdit, IndentLevel},
10 make, AstNode, PathSegmentKind, VisibilityOwner, 12 make, AstNode, PathSegmentKind, VisibilityOwner,
11 }, 13 },
12 InsertPosition, SyntaxElement, SyntaxNode, 14 AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken,
13}; 15};
14use test_utils::mark; 16use test_utils::mark;
15 17
16#[derive(Debug)] 18#[derive(Debug, Clone)]
17pub(crate) enum ImportScope { 19pub enum ImportScope {
18 File(ast::SourceFile), 20 File(ast::SourceFile),
19 Module(ast::ItemList), 21 Module(ast::ItemList),
20} 22}
@@ -31,14 +33,14 @@ impl ImportScope {
31 } 33 }
32 34
33 /// Determines the containing syntax node in which to insert a `use` statement affecting `position`. 35 /// Determines the containing syntax node in which to insert a `use` statement affecting `position`.
34 pub(crate) fn find_insert_use_container( 36 pub fn find_insert_use_container(
35 position: &SyntaxNode, 37 position: &SyntaxNode,
36 ctx: &crate::assist_context::AssistContext, 38 sema: &Semantics<'_, RootDatabase>,
37 ) -> Option<Self> { 39 ) -> Option<Self> {
38 ctx.sema.ancestors_with_macros(position.clone()).find_map(Self::from) 40 sema.ancestors_with_macros(position.clone()).find_map(Self::from)
39 } 41 }
40 42
41 pub(crate) fn as_syntax_node(&self) -> &SyntaxNode { 43 pub fn as_syntax_node(&self) -> &SyntaxNode {
42 match self { 44 match self {
43 ImportScope::File(file) => file.syntax(), 45 ImportScope::File(file) => file.syntax(),
44 ImportScope::Module(item_list) => item_list.syntax(), 46 ImportScope::Module(item_list) => item_list.syntax(),
@@ -63,29 +65,32 @@ impl ImportScope {
63 } 65 }
64 } 66 }
65 67
66 fn insert_pos_after_inner_attribute(&self) -> (InsertPosition<SyntaxElement>, AddBlankLine) { 68 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 69 self.as_syntax_node()
68 match self 70 .children_with_tokens()
69 .as_syntax_node() 71 .filter(|child| match child {
70 .children() 72 NodeOrToken::Node(node) => is_inner_attribute(node.clone()),
71 // no flat_map here cause we want to short circuit the iterator 73 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 }) 74 })
76 .last() 75 .last()
77 .flatten() 76 .map(|last_inner_element| {
78 { 77 (InsertPosition::After(last_inner_element.into()), AddBlankLine::BeforeTwice)
79 Some(attr) => { 78 })
80 (InsertPosition::After(attr.syntax().clone().into()), AddBlankLine::BeforeTwice) 79 .unwrap_or_else(|| self.first_insert_pos())
81 }
82 None => self.first_insert_pos(),
83 }
84 } 80 }
85} 81}
86 82
83fn is_inner_attribute(node: SyntaxNode) -> bool {
84 ast::Attr::cast(node).map(|attr| attr.kind()) == Some(ast::AttrKind::Inner)
85}
86
87fn is_inner_comment(token: SyntaxToken) -> bool {
88 ast::Comment::cast(token).and_then(|comment| comment.kind().doc)
89 == Some(ast::CommentPlacement::Inner)
90}
91
87/// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur. 92/// 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>( 93pub fn insert_use<'a>(
89 scope: &ImportScope, 94 scope: &ImportScope,
90 path: ast::Path, 95 path: ast::Path,
91 merge: Option<MergeBehaviour>, 96 merge: Option<MergeBehaviour>,
@@ -558,7 +563,7 @@ fn find_insert_position(
558 (InsertPosition::After(node.into()), AddBlankLine::BeforeTwice) 563 (InsertPosition::After(node.into()), AddBlankLine::BeforeTwice)
559 } 564 }
560 // there are no imports in this file at all 565 // there are no imports in this file at all
561 None => scope.insert_pos_after_inner_attribute(), 566 None => scope.insert_pos_after_last_inner_element(),
562 }, 567 },
563 } 568 }
564 } 569 }
@@ -830,12 +835,67 @@ use foo::bar;",
830 "foo::bar", 835 "foo::bar",
831 r"#![allow(unused_imports)] 836 r"#![allow(unused_imports)]
832 837
838#![no_std]
833fn main() {}", 839fn main() {}",
834 r"#![allow(unused_imports)] 840 r"#![allow(unused_imports)]
835 841
836use foo::bar; 842#![no_std]
837 843
844use foo::bar;
838fn main() {}", 845fn main() {}",
846 );
847 }
848
849 #[test]
850 fn inserts_after_single_line_inner_comments() {
851 check_none(
852 "foo::bar::Baz",
853 "//! Single line inner comments do not allow any code before them.",
854 r#"//! Single line inner comments do not allow any code before them.
855
856use foo::bar::Baz;"#,
857 );
858 }
859
860 #[test]
861 fn inserts_after_multiline_inner_comments() {
862 check_none(
863 "foo::bar::Baz",
864 r#"/*! Multiline inner comments do not allow any code before them. */
865
866/*! Still an inner comment, cannot place any code before. */
867fn main() {}"#,
868 r#"/*! Multiline inner comments do not allow any code before them. */
869
870/*! Still an inner comment, cannot place any code before. */
871
872use foo::bar::Baz;
873fn main() {}"#,
874 )
875 }
876
877 #[test]
878 fn inserts_after_all_inner_items() {
879 check_none(
880 "foo::bar::Baz",
881 r#"#![allow(unused_imports)]
882/*! Multiline line comment 2 */
883
884
885//! Single line comment 1
886#![no_std]
887//! Single line comment 2
888fn main() {}"#,
889 r#"#![allow(unused_imports)]
890/*! Multiline line comment 2 */
891
892
893//! Single line comment 1
894#![no_std]
895//! Single line comment 2
896
897use foo::bar::Baz;
898fn main() {}"#,
839 ) 899 )
840 } 900 }
841 901