diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ide_assists/src/handlers/auto_import.rs | 61 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs | 2 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs | 13 | ||||
-rw-r--r-- | crates/ide_assists/src/tests.rs | 1 | ||||
-rw-r--r-- | crates/ide_completion/src/item.rs | 2 | ||||
-rw-r--r-- | crates/ide_completion/src/tests.rs | 1 | ||||
-rw-r--r-- | crates/ide_db/src/helpers/insert_use.rs | 78 | ||||
-rw-r--r-- | crates/ide_db/src/helpers/insert_use/tests.rs | 72 | ||||
-rw-r--r-- | crates/rust-analyzer/src/config.rs | 4 | ||||
-rw-r--r-- | crates/rust-analyzer/src/integrated_benchmarks.rs | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/src/to_proto.rs | 1 | ||||
-rw-r--r-- | crates/syntax/src/ast/node_ext.rs | 17 |
12 files changed, 206 insertions, 48 deletions
diff --git a/crates/ide_assists/src/handlers/auto_import.rs b/crates/ide_assists/src/handlers/auto_import.rs index d4748ef3a..8df8b060d 100644 --- a/crates/ide_assists/src/handlers/auto_import.rs +++ b/crates/ide_assists/src/handlers/auto_import.rs | |||
@@ -103,8 +103,9 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
103 | let scope = match scope.clone() { | 103 | let scope = match scope.clone() { |
104 | ImportScope::File(it) => ImportScope::File(builder.make_mut(it)), | 104 | ImportScope::File(it) => ImportScope::File(builder.make_mut(it)), |
105 | ImportScope::Module(it) => ImportScope::Module(builder.make_mut(it)), | 105 | ImportScope::Module(it) => ImportScope::Module(builder.make_mut(it)), |
106 | ImportScope::Block(it) => ImportScope::Block(builder.make_mut(it)), | ||
106 | }; | 107 | }; |
107 | insert_use(&scope, mod_path_to_ast(&import.import_path), ctx.config.insert_use); | 108 | insert_use(&scope, mod_path_to_ast(&import.import_path), &ctx.config.insert_use); |
108 | }, | 109 | }, |
109 | ); | 110 | ); |
110 | } | 111 | } |
@@ -994,4 +995,62 @@ const _: () = { | |||
994 | "#, | 995 | "#, |
995 | ); | 996 | ); |
996 | } | 997 | } |
998 | |||
999 | #[test] | ||
1000 | fn respects_cfg_attr() { | ||
1001 | check_assist( | ||
1002 | auto_import, | ||
1003 | r#" | ||
1004 | mod bar { | ||
1005 | pub struct Bar; | ||
1006 | } | ||
1007 | |||
1008 | #[cfg(test)] | ||
1009 | fn foo() { | ||
1010 | Bar$0 | ||
1011 | } | ||
1012 | "#, | ||
1013 | r#" | ||
1014 | mod bar { | ||
1015 | pub struct Bar; | ||
1016 | } | ||
1017 | |||
1018 | #[cfg(test)] | ||
1019 | fn foo() { | ||
1020 | use bar::Bar; | ||
1021 | |||
1022 | Bar | ||
1023 | } | ||
1024 | "#, | ||
1025 | ); | ||
1026 | } | ||
1027 | |||
1028 | #[test] | ||
1029 | fn respects_cfg_attr2() { | ||
1030 | check_assist( | ||
1031 | auto_import, | ||
1032 | r#" | ||
1033 | mod bar { | ||
1034 | pub struct Bar; | ||
1035 | } | ||
1036 | |||
1037 | #[cfg(test)] | ||
1038 | const FOO: Bar = { | ||
1039 | Bar$0 | ||
1040 | } | ||
1041 | "#, | ||
1042 | r#" | ||
1043 | mod bar { | ||
1044 | pub struct Bar; | ||
1045 | } | ||
1046 | |||
1047 | #[cfg(test)] | ||
1048 | const FOO: Bar = { | ||
1049 | use bar::Bar; | ||
1050 | |||
1051 | Bar | ||
1052 | } | ||
1053 | "#, | ||
1054 | ); | ||
1055 | } | ||
997 | } | 1056 | } |
diff --git a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs index 6c6ff16c2..430710448 100644 --- a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs | |||
@@ -236,7 +236,7 @@ fn apply_references( | |||
236 | import: Option<(ImportScope, hir::ModPath)>, | 236 | import: Option<(ImportScope, hir::ModPath)>, |
237 | ) { | 237 | ) { |
238 | if let Some((scope, path)) = import { | 238 | if let Some((scope, path)) = import { |
239 | insert_use(&scope, mod_path_to_ast(&path), insert_use_cfg); | 239 | insert_use(&scope, mod_path_to_ast(&path), &insert_use_cfg); |
240 | } | 240 | } |
241 | // deep clone to prevent cycle | 241 | // deep clone to prevent cycle |
242 | let path = make::path_from_segments(iter::once(segment.clone_subtree()), false); | 242 | let path = make::path_from_segments(iter::once(segment.clone_subtree()), false); |
diff --git a/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs index 39f5eb4ff..26778ee74 100644 --- a/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs +++ b/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs | |||
@@ -32,7 +32,6 @@ pub(crate) fn replace_qualified_name_with_use( | |||
32 | 32 | ||
33 | let target = path.syntax().text_range(); | 33 | let target = path.syntax().text_range(); |
34 | let scope = ImportScope::find_insert_use_container_with_macros(path.syntax(), &ctx.sema)?; | 34 | let scope = ImportScope::find_insert_use_container_with_macros(path.syntax(), &ctx.sema)?; |
35 | let syntax = scope.as_syntax_node(); | ||
36 | acc.add( | 35 | acc.add( |
37 | AssistId("replace_qualified_name_with_use", AssistKind::RefactorRewrite), | 36 | AssistId("replace_qualified_name_with_use", AssistKind::RefactorRewrite), |
38 | "Replace qualified path with use", | 37 | "Replace qualified path with use", |
@@ -40,11 +39,13 @@ pub(crate) fn replace_qualified_name_with_use( | |||
40 | |builder| { | 39 | |builder| { |
41 | // Now that we've brought the name into scope, re-qualify all paths that could be | 40 | // Now that we've brought the name into scope, re-qualify all paths that could be |
42 | // affected (that is, all paths inside the node we added the `use` to). | 41 | // affected (that is, all paths inside the node we added the `use` to). |
43 | let syntax = builder.make_syntax_mut(syntax.clone()); | 42 | let scope = match scope { |
44 | if let Some(ref import_scope) = ImportScope::from(syntax.clone()) { | 43 | ImportScope::File(it) => ImportScope::File(builder.make_mut(it)), |
45 | shorten_paths(&syntax, &path.clone_for_update()); | 44 | ImportScope::Module(it) => ImportScope::Module(builder.make_mut(it)), |
46 | insert_use(import_scope, path, ctx.config.insert_use); | 45 | ImportScope::Block(it) => ImportScope::Block(builder.make_mut(it)), |
47 | } | 46 | }; |
47 | shorten_paths(scope.as_syntax_node(), &path.clone_for_update()); | ||
48 | insert_use(&scope, path, &ctx.config.insert_use); | ||
48 | }, | 49 | }, |
49 | ) | 50 | ) |
50 | } | 51 | } |
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs index 4e96ff1ec..841537c77 100644 --- a/crates/ide_assists/src/tests.rs +++ b/crates/ide_assists/src/tests.rs | |||
@@ -28,6 +28,7 @@ pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig { | |||
28 | prefix_kind: hir::PrefixKind::Plain, | 28 | prefix_kind: hir::PrefixKind::Plain, |
29 | enforce_granularity: true, | 29 | enforce_granularity: true, |
30 | group: true, | 30 | group: true, |
31 | skip_glob_imports: true, | ||
31 | }, | 32 | }, |
32 | }; | 33 | }; |
33 | 34 | ||
diff --git a/crates/ide_completion/src/item.rs b/crates/ide_completion/src/item.rs index 99edb9499..ae63d132e 100644 --- a/crates/ide_completion/src/item.rs +++ b/crates/ide_completion/src/item.rs | |||
@@ -378,7 +378,7 @@ impl ImportEdit { | |||
378 | let _p = profile::span("ImportEdit::to_text_edit"); | 378 | let _p = profile::span("ImportEdit::to_text_edit"); |
379 | 379 | ||
380 | let new_ast = self.scope.clone_for_update(); | 380 | let new_ast = self.scope.clone_for_update(); |
381 | insert_use::insert_use(&new_ast, mod_path_to_ast(&self.import.import_path), cfg); | 381 | insert_use::insert_use(&new_ast, mod_path_to_ast(&self.import.import_path), &cfg); |
382 | let mut import_insert = TextEdit::builder(); | 382 | let mut import_insert = TextEdit::builder(); |
383 | algo::diff(self.scope.as_syntax_node(), new_ast.as_syntax_node()) | 383 | algo::diff(self.scope.as_syntax_node(), new_ast.as_syntax_node()) |
384 | .into_text_edit(&mut import_insert); | 384 | .into_text_edit(&mut import_insert); |
diff --git a/crates/ide_completion/src/tests.rs b/crates/ide_completion/src/tests.rs index 1ea6017ce..211c89c40 100644 --- a/crates/ide_completion/src/tests.rs +++ b/crates/ide_completion/src/tests.rs | |||
@@ -36,6 +36,7 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig { | |||
36 | prefix_kind: PrefixKind::Plain, | 36 | prefix_kind: PrefixKind::Plain, |
37 | enforce_granularity: true, | 37 | enforce_granularity: true, |
38 | group: true, | 38 | group: true, |
39 | skip_glob_imports: true, | ||
39 | }, | 40 | }, |
40 | }; | 41 | }; |
41 | 42 | ||
diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs index 10bbafe77..e6b4832e7 100644 --- a/crates/ide_db/src/helpers/insert_use.rs +++ b/crates/ide_db/src/helpers/insert_use.rs | |||
@@ -5,7 +5,7 @@ use hir::Semantics; | |||
5 | use syntax::{ | 5 | use syntax::{ |
6 | algo, | 6 | algo, |
7 | ast::{self, make, AstNode, AttrsOwner, ModuleItemOwner, PathSegmentKind, VisibilityOwner}, | 7 | ast::{self, make, AstNode, AttrsOwner, ModuleItemOwner, PathSegmentKind, VisibilityOwner}, |
8 | ted, AstToken, Direction, NodeOrToken, SyntaxNode, SyntaxToken, | 8 | match_ast, ted, AstToken, Direction, NodeOrToken, SyntaxNode, SyntaxToken, |
9 | }; | 9 | }; |
10 | 10 | ||
11 | use crate::{ | 11 | use crate::{ |
@@ -36,22 +36,39 @@ pub struct InsertUseConfig { | |||
36 | pub enforce_granularity: bool, | 36 | pub enforce_granularity: bool, |
37 | pub prefix_kind: PrefixKind, | 37 | pub prefix_kind: PrefixKind, |
38 | pub group: bool, | 38 | pub group: bool, |
39 | pub skip_glob_imports: bool, | ||
39 | } | 40 | } |
40 | 41 | ||
41 | #[derive(Debug, Clone)] | 42 | #[derive(Debug, Clone)] |
42 | pub enum ImportScope { | 43 | pub enum ImportScope { |
43 | File(ast::SourceFile), | 44 | File(ast::SourceFile), |
44 | Module(ast::ItemList), | 45 | Module(ast::ItemList), |
46 | Block(ast::BlockExpr), | ||
45 | } | 47 | } |
46 | 48 | ||
47 | impl ImportScope { | 49 | impl ImportScope { |
48 | pub fn from(syntax: SyntaxNode) -> Option<Self> { | 50 | fn from(syntax: SyntaxNode) -> Option<Self> { |
49 | if let Some(module) = ast::Module::cast(syntax.clone()) { | 51 | fn contains_cfg_attr(attrs: &dyn AttrsOwner) -> bool { |
50 | module.item_list().map(ImportScope::Module) | 52 | attrs |
51 | } else if let this @ Some(_) = ast::SourceFile::cast(syntax.clone()) { | 53 | .attrs() |
52 | this.map(ImportScope::File) | 54 | .any(|attr| attr.as_simple_call().map_or(false, |(ident, _)| ident == "cfg")) |
53 | } else { | 55 | } |
54 | ast::ItemList::cast(syntax).map(ImportScope::Module) | 56 | match_ast! { |
57 | match syntax { | ||
58 | ast::Module(module) => module.item_list().map(ImportScope::Module), | ||
59 | ast::SourceFile(file) => Some(ImportScope::File(file)), | ||
60 | ast::Fn(func) => contains_cfg_attr(&func).then(|| func.body().map(ImportScope::Block)).flatten(), | ||
61 | ast::Const(konst) => contains_cfg_attr(&konst).then(|| match konst.body()? { | ||
62 | ast::Expr::BlockExpr(block) => Some(block), | ||
63 | _ => None, | ||
64 | }).flatten().map(ImportScope::Block), | ||
65 | ast::Static(statik) => contains_cfg_attr(&statik).then(|| match statik.body()? { | ||
66 | ast::Expr::BlockExpr(block) => Some(block), | ||
67 | _ => None, | ||
68 | }).flatten().map(ImportScope::Block), | ||
69 | _ => None, | ||
70 | |||
71 | } | ||
55 | } | 72 | } |
56 | } | 73 | } |
57 | 74 | ||
@@ -72,6 +89,7 @@ impl ImportScope { | |||
72 | match self { | 89 | match self { |
73 | ImportScope::File(file) => file.syntax(), | 90 | ImportScope::File(file) => file.syntax(), |
74 | ImportScope::Module(item_list) => item_list.syntax(), | 91 | ImportScope::Module(item_list) => item_list.syntax(), |
92 | ImportScope::Block(block) => block.syntax(), | ||
75 | } | 93 | } |
76 | } | 94 | } |
77 | 95 | ||
@@ -79,6 +97,7 @@ impl ImportScope { | |||
79 | match self { | 97 | match self { |
80 | ImportScope::File(file) => ImportScope::File(file.clone_for_update()), | 98 | ImportScope::File(file) => ImportScope::File(file.clone_for_update()), |
81 | ImportScope::Module(item_list) => ImportScope::Module(item_list.clone_for_update()), | 99 | ImportScope::Module(item_list) => ImportScope::Module(item_list.clone_for_update()), |
100 | ImportScope::Block(block) => ImportScope::Block(block.clone_for_update()), | ||
82 | } | 101 | } |
83 | } | 102 | } |
84 | 103 | ||
@@ -95,6 +114,7 @@ impl ImportScope { | |||
95 | let mut use_stmts = match self { | 114 | let mut use_stmts = match self { |
96 | ImportScope::File(f) => f.items(), | 115 | ImportScope::File(f) => f.items(), |
97 | ImportScope::Module(m) => m.items(), | 116 | ImportScope::Module(m) => m.items(), |
117 | ImportScope::Block(b) => b.items(), | ||
98 | } | 118 | } |
99 | .filter_map(use_stmt); | 119 | .filter_map(use_stmt); |
100 | let mut res = ImportGranularityGuess::Unknown; | 120 | let mut res = ImportGranularityGuess::Unknown; |
@@ -153,7 +173,7 @@ enum ImportGranularityGuess { | |||
153 | } | 173 | } |
154 | 174 | ||
155 | /// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur. | 175 | /// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur. |
156 | pub fn insert_use<'a>(scope: &ImportScope, path: ast::Path, cfg: InsertUseConfig) { | 176 | pub fn insert_use<'a>(scope: &ImportScope, path: ast::Path, cfg: &InsertUseConfig) { |
157 | let _p = profile::span("insert_use"); | 177 | let _p = profile::span("insert_use"); |
158 | let mut mb = match cfg.granularity { | 178 | let mut mb = match cfg.granularity { |
159 | ImportGranularity::Crate => Some(MergeBehavior::Crate), | 179 | ImportGranularity::Crate => Some(MergeBehavior::Crate), |
@@ -175,7 +195,10 @@ pub fn insert_use<'a>(scope: &ImportScope, path: ast::Path, cfg: InsertUseConfig | |||
175 | make::use_(None, make::use_tree(path.clone(), None, None, false)).clone_for_update(); | 195 | make::use_(None, make::use_tree(path.clone(), None, None, false)).clone_for_update(); |
176 | // merge into existing imports if possible | 196 | // merge into existing imports if possible |
177 | if let Some(mb) = mb { | 197 | if let Some(mb) = mb { |
178 | for existing_use in scope.as_syntax_node().children().filter_map(ast::Use::cast) { | 198 | let filter = |it: &_| !(cfg.skip_glob_imports && ast::Use::is_simple_glob(it)); |
199 | for existing_use in | ||
200 | scope.as_syntax_node().children().filter_map(ast::Use::cast).filter(filter) | ||
201 | { | ||
179 | if let Some(merged) = try_merge_imports(&existing_use, &use_item, mb) { | 202 | if let Some(merged) = try_merge_imports(&existing_use, &use_item, mb) { |
180 | ted::replace(existing_use.syntax(), merged.syntax()); | 203 | ted::replace(existing_use.syntax(), merged.syntax()); |
181 | return; | 204 | return; |
@@ -315,28 +338,29 @@ fn insert_use_( | |||
315 | ted::insert(ted::Position::after(last_inner_element), make::tokens::single_newline()); | 338 | ted::insert(ted::Position::after(last_inner_element), make::tokens::single_newline()); |
316 | return; | 339 | return; |
317 | } | 340 | } |
318 | match scope { | 341 | let l_curly = match scope { |
319 | ImportScope::File(_) => { | 342 | ImportScope::File(_) => { |
320 | cov_mark::hit!(insert_group_empty_file); | 343 | cov_mark::hit!(insert_group_empty_file); |
321 | ted::insert(ted::Position::first_child_of(scope_syntax), make::tokens::blank_line()); | 344 | ted::insert(ted::Position::first_child_of(scope_syntax), make::tokens::blank_line()); |
322 | ted::insert(ted::Position::first_child_of(scope_syntax), use_item.syntax()) | 345 | ted::insert(ted::Position::first_child_of(scope_syntax), use_item.syntax()); |
346 | return; | ||
323 | } | 347 | } |
348 | // don't insert the imports before the item list/block expr's opening curly brace | ||
349 | ImportScope::Module(item_list) => item_list.l_curly_token(), | ||
324 | // don't insert the imports before the item list's opening curly brace | 350 | // don't insert the imports before the item list's opening curly brace |
325 | ImportScope::Module(item_list) => match item_list.l_curly_token() { | 351 | ImportScope::Block(block) => block.l_curly_token(), |
326 | Some(b) => { | 352 | }; |
327 | cov_mark::hit!(insert_group_empty_module); | 353 | match l_curly { |
328 | ted::insert(ted::Position::after(&b), make::tokens::single_newline()); | 354 | Some(b) => { |
329 | ted::insert(ted::Position::after(&b), use_item.syntax()); | 355 | cov_mark::hit!(insert_group_empty_module); |
330 | } | 356 | ted::insert(ted::Position::after(&b), make::tokens::single_newline()); |
331 | None => { | 357 | ted::insert(ted::Position::after(&b), use_item.syntax()); |
332 | // This should never happens, broken module syntax node | 358 | } |
333 | ted::insert( | 359 | None => { |
334 | ted::Position::first_child_of(scope_syntax), | 360 | // This should never happens, broken module syntax node |
335 | make::tokens::blank_line(), | 361 | ted::insert(ted::Position::first_child_of(scope_syntax), make::tokens::blank_line()); |
336 | ); | 362 | ted::insert(ted::Position::first_child_of(scope_syntax), use_item.syntax()); |
337 | ted::insert(ted::Position::first_child_of(scope_syntax), use_item.syntax()); | 363 | } |
338 | } | ||
339 | }, | ||
340 | } | 364 | } |
341 | } | 365 | } |
342 | 366 | ||
diff --git a/crates/ide_db/src/helpers/insert_use/tests.rs b/crates/ide_db/src/helpers/insert_use/tests.rs index 5a88ec742..263edcdc9 100644 --- a/crates/ide_db/src/helpers/insert_use/tests.rs +++ b/crates/ide_db/src/helpers/insert_use/tests.rs | |||
@@ -4,6 +4,23 @@ use hir::PrefixKind; | |||
4 | use test_utils::assert_eq_text; | 4 | use test_utils::assert_eq_text; |
5 | 5 | ||
6 | #[test] | 6 | #[test] |
7 | fn insert_skips_lone_glob_imports() { | ||
8 | check( | ||
9 | "use foo::baz::A", | ||
10 | r" | ||
11 | use foo::bar::*; | ||
12 | ", | ||
13 | r" | ||
14 | use foo::bar::*; | ||
15 | use foo::baz::A; | ||
16 | ", | ||
17 | ImportGranularity::Crate, | ||
18 | false, | ||
19 | false, | ||
20 | ); | ||
21 | } | ||
22 | |||
23 | #[test] | ||
7 | fn insert_not_group() { | 24 | fn insert_not_group() { |
8 | cov_mark::check!(insert_no_grouping_last); | 25 | cov_mark::check!(insert_no_grouping_last); |
9 | check( | 26 | check( |
@@ -534,17 +551,37 @@ fn merge_groups_self() { | |||
534 | 551 | ||
535 | #[test] | 552 | #[test] |
536 | fn merge_mod_into_glob() { | 553 | fn merge_mod_into_glob() { |
537 | check_crate( | 554 | check_with_config( |
538 | "token::TokenKind", | 555 | "token::TokenKind", |
539 | r"use token::TokenKind::*;", | 556 | r"use token::TokenKind::*;", |
540 | r"use token::TokenKind::{*, self};", | 557 | r"use token::TokenKind::{*, self};", |
558 | false, | ||
559 | &InsertUseConfig { | ||
560 | granularity: ImportGranularity::Crate, | ||
561 | enforce_granularity: true, | ||
562 | prefix_kind: PrefixKind::Plain, | ||
563 | group: false, | ||
564 | skip_glob_imports: false, | ||
565 | }, | ||
541 | ) | 566 | ) |
542 | // FIXME: have it emit `use token::TokenKind::{self, *}`? | 567 | // FIXME: have it emit `use token::TokenKind::{self, *}`? |
543 | } | 568 | } |
544 | 569 | ||
545 | #[test] | 570 | #[test] |
546 | fn merge_self_glob() { | 571 | fn merge_self_glob() { |
547 | check_crate("self", r"use self::*;", r"use self::{*, self};") | 572 | check_with_config( |
573 | "self", | ||
574 | r"use self::*;", | ||
575 | r"use self::{*, self};", | ||
576 | false, | ||
577 | &InsertUseConfig { | ||
578 | granularity: ImportGranularity::Crate, | ||
579 | enforce_granularity: true, | ||
580 | prefix_kind: PrefixKind::Plain, | ||
581 | group: false, | ||
582 | skip_glob_imports: false, | ||
583 | }, | ||
584 | ) | ||
548 | // FIXME: have it emit `use {self, *}`? | 585 | // FIXME: have it emit `use {self, *}`? |
549 | } | 586 | } |
550 | 587 | ||
@@ -757,13 +794,12 @@ use foo::bar::qux; | |||
757 | ); | 794 | ); |
758 | } | 795 | } |
759 | 796 | ||
760 | fn check( | 797 | fn check_with_config( |
761 | path: &str, | 798 | path: &str, |
762 | ra_fixture_before: &str, | 799 | ra_fixture_before: &str, |
763 | ra_fixture_after: &str, | 800 | ra_fixture_after: &str, |
764 | granularity: ImportGranularity, | ||
765 | module: bool, | 801 | module: bool, |
766 | group: bool, | 802 | config: &InsertUseConfig, |
767 | ) { | 803 | ) { |
768 | let mut syntax = ast::SourceFile::parse(ra_fixture_before).tree().syntax().clone(); | 804 | let mut syntax = ast::SourceFile::parse(ra_fixture_before).tree().syntax().clone(); |
769 | if module { | 805 | if module { |
@@ -777,18 +813,32 @@ fn check( | |||
777 | .find_map(ast::Path::cast) | 813 | .find_map(ast::Path::cast) |
778 | .unwrap(); | 814 | .unwrap(); |
779 | 815 | ||
780 | insert_use( | 816 | insert_use(&file, path, config); |
781 | &file, | 817 | let result = file.as_syntax_node().to_string(); |
818 | assert_eq_text!(ra_fixture_after, &result); | ||
819 | } | ||
820 | |||
821 | fn check( | ||
822 | path: &str, | ||
823 | ra_fixture_before: &str, | ||
824 | ra_fixture_after: &str, | ||
825 | granularity: ImportGranularity, | ||
826 | module: bool, | ||
827 | group: bool, | ||
828 | ) { | ||
829 | check_with_config( | ||
782 | path, | 830 | path, |
783 | InsertUseConfig { | 831 | ra_fixture_before, |
832 | ra_fixture_after, | ||
833 | module, | ||
834 | &InsertUseConfig { | ||
784 | granularity, | 835 | granularity, |
785 | enforce_granularity: true, | 836 | enforce_granularity: true, |
786 | prefix_kind: PrefixKind::Plain, | 837 | prefix_kind: PrefixKind::Plain, |
787 | group, | 838 | group, |
839 | skip_glob_imports: true, | ||
788 | }, | 840 | }, |
789 | ); | 841 | ) |
790 | let result = file.as_syntax_node().to_string(); | ||
791 | assert_eq_text!(ra_fixture_after, &result); | ||
792 | } | 842 | } |
793 | 843 | ||
794 | fn check_crate(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { | 844 | fn check_crate(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index c4757a1cb..16c295639 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -44,6 +44,9 @@ config_data! { | |||
44 | assist_importPrefix: ImportPrefixDef = "\"plain\"", | 44 | assist_importPrefix: ImportPrefixDef = "\"plain\"", |
45 | /// Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines. | 45 | /// Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines. |
46 | assist_importGroup: bool = "true", | 46 | assist_importGroup: bool = "true", |
47 | /// Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`. | ||
48 | assist_allowMergingIntoGlobImports: bool = "true", | ||
49 | |||
47 | /// Show function name and docs in parameter hints. | 50 | /// Show function name and docs in parameter hints. |
48 | callInfo_full: bool = "true", | 51 | callInfo_full: bool = "true", |
49 | 52 | ||
@@ -672,6 +675,7 @@ impl Config { | |||
672 | ImportPrefixDef::BySelf => PrefixKind::BySelf, | 675 | ImportPrefixDef::BySelf => PrefixKind::BySelf, |
673 | }, | 676 | }, |
674 | group: self.data.assist_importGroup, | 677 | group: self.data.assist_importGroup, |
678 | skip_glob_imports: !self.data.assist_allowMergingIntoGlobImports, | ||
675 | } | 679 | } |
676 | } | 680 | } |
677 | pub fn completion(&self) -> CompletionConfig { | 681 | pub fn completion(&self) -> CompletionConfig { |
diff --git a/crates/rust-analyzer/src/integrated_benchmarks.rs b/crates/rust-analyzer/src/integrated_benchmarks.rs index 8ddeb59f7..f8afc930a 100644 --- a/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/crates/rust-analyzer/src/integrated_benchmarks.rs | |||
@@ -143,6 +143,7 @@ fn integrated_completion_benchmark() { | |||
143 | prefix_kind: hir::PrefixKind::ByCrate, | 143 | prefix_kind: hir::PrefixKind::ByCrate, |
144 | enforce_granularity: true, | 144 | enforce_granularity: true, |
145 | group: true, | 145 | group: true, |
146 | skip_glob_imports: true, | ||
146 | }, | 147 | }, |
147 | }; | 148 | }; |
148 | let position = | 149 | let position = |
@@ -178,6 +179,7 @@ fn integrated_completion_benchmark() { | |||
178 | prefix_kind: hir::PrefixKind::ByCrate, | 179 | prefix_kind: hir::PrefixKind::ByCrate, |
179 | enforce_granularity: true, | 180 | enforce_granularity: true, |
180 | group: true, | 181 | group: true, |
182 | skip_glob_imports: true, | ||
181 | }, | 183 | }, |
182 | }; | 184 | }; |
183 | let position = | 185 | let position = |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index e53cd3c7b..310d8c6d2 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -1187,6 +1187,7 @@ mod tests { | |||
1187 | prefix_kind: PrefixKind::Plain, | 1187 | prefix_kind: PrefixKind::Plain, |
1188 | enforce_granularity: true, | 1188 | enforce_granularity: true, |
1189 | group: true, | 1189 | group: true, |
1190 | skip_glob_imports: true, | ||
1190 | }, | 1191 | }, |
1191 | }, | 1192 | }, |
1192 | ide_db::base_db::FilePosition { file_id, offset }, | 1193 | ide_db::base_db::FilePosition { file_id, offset }, |
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index b057e6624..e33e5bb03 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs | |||
@@ -8,7 +8,7 @@ use parser::SyntaxKind; | |||
8 | use rowan::{GreenNodeData, GreenTokenData}; | 8 | use rowan::{GreenNodeData, GreenTokenData}; |
9 | 9 | ||
10 | use crate::{ | 10 | use crate::{ |
11 | ast::{self, support, AstNode, AstToken, AttrsOwner, NameOwner, SyntaxNode}, | 11 | ast::{self, support, AstChildren, AstNode, AstToken, AttrsOwner, NameOwner, SyntaxNode}, |
12 | NodeOrToken, SmolStr, SyntaxElement, SyntaxToken, TokenText, T, | 12 | NodeOrToken, SmolStr, SyntaxElement, SyntaxToken, TokenText, T, |
13 | }; | 13 | }; |
14 | 14 | ||
@@ -45,6 +45,12 @@ fn text_of_first_token(node: &SyntaxNode) -> TokenText<'_> { | |||
45 | } | 45 | } |
46 | } | 46 | } |
47 | 47 | ||
48 | impl ast::BlockExpr { | ||
49 | pub fn items(&self) -> AstChildren<ast::Item> { | ||
50 | support::children(self.syntax()) | ||
51 | } | ||
52 | } | ||
53 | |||
48 | #[derive(Debug, PartialEq, Eq, Clone)] | 54 | #[derive(Debug, PartialEq, Eq, Clone)] |
49 | pub enum Macro { | 55 | pub enum Macro { |
50 | MacroRules(ast::MacroRules), | 56 | MacroRules(ast::MacroRules), |
@@ -281,6 +287,15 @@ impl ast::Path { | |||
281 | successors(self.qualifier(), |p| p.qualifier()) | 287 | successors(self.qualifier(), |p| p.qualifier()) |
282 | } | 288 | } |
283 | } | 289 | } |
290 | |||
291 | impl ast::Use { | ||
292 | pub fn is_simple_glob(&self) -> bool { | ||
293 | self.use_tree() | ||
294 | .map(|use_tree| use_tree.use_tree_list().is_none() && use_tree.star_token().is_some()) | ||
295 | .unwrap_or(false) | ||
296 | } | ||
297 | } | ||
298 | |||
284 | impl ast::UseTree { | 299 | impl ast::UseTree { |
285 | pub fn is_simple_path(&self) -> bool { | 300 | pub fn is_simple_path(&self) -> bool { |
286 | self.use_tree_list().is_none() && self.star_token().is_none() | 301 | self.use_tree_list().is_none() && self.star_token().is_none() |