diff options
37 files changed, 1096 insertions, 536 deletions
diff --git a/Cargo.lock b/Cargo.lock index 35713a0c4..f01544324 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -1904,9 +1904,9 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" | |||
1904 | 1904 | ||
1905 | [[package]] | 1905 | [[package]] |
1906 | name = "ungrammar" | 1906 | name = "ungrammar" |
1907 | version = "1.9.2" | 1907 | version = "1.9.3" |
1908 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1908 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1909 | checksum = "58a02e2041a872d56354e843e8e86e6b946fc8e7dc32982fcdc335e29eb4cc8b" | 1909 | checksum = "f5901372c0f3a6a1a9d880aef134c8eaf5e54409343637508c0a344270b42d7b" |
1910 | 1910 | ||
1911 | [[package]] | 1911 | [[package]] |
1912 | name = "unicase" | 1912 | name = "unicase" |
diff --git a/crates/assists/src/handlers/auto_import.rs b/crates/assists/src/handlers/auto_import.rs index 4e2a4fcd9..e93901cb3 100644 --- a/crates/assists/src/handlers/auto_import.rs +++ b/crates/assists/src/handlers/auto_import.rs | |||
@@ -3,7 +3,7 @@ use ide_db::helpers::{ | |||
3 | insert_use::{insert_use, ImportScope}, | 3 | insert_use::{insert_use, ImportScope}, |
4 | mod_path_to_ast, | 4 | mod_path_to_ast, |
5 | }; | 5 | }; |
6 | use syntax::ast; | 6 | use syntax::{ast, AstNode, SyntaxNode}; |
7 | 7 | ||
8 | use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel}; | 8 | use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel}; |
9 | 9 | ||
@@ -82,25 +82,16 @@ use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel}; | |||
82 | // # pub mod std { pub mod collections { pub struct HashMap { } } } | 82 | // # pub mod std { pub mod collections { pub struct HashMap { } } } |
83 | // ``` | 83 | // ``` |
84 | pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 84 | pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
85 | let import_assets = | 85 | let (import_assets, syntax_under_caret) = find_importable_node(ctx)?; |
86 | if let Some(path_under_caret) = ctx.find_node_at_offset_with_descend::<ast::Path>() { | 86 | let proposed_imports = |
87 | ImportAssets::for_regular_path(path_under_caret, &ctx.sema) | 87 | import_assets.search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind); |
88 | } else if let Some(method_under_caret) = | ||
89 | ctx.find_node_at_offset_with_descend::<ast::MethodCallExpr>() | ||
90 | { | ||
91 | ImportAssets::for_method_call(method_under_caret, &ctx.sema) | ||
92 | } else { | ||
93 | None | ||
94 | }?; | ||
95 | let proposed_imports = import_assets.search_for_imports(&ctx.sema, &ctx.config.insert_use); | ||
96 | if proposed_imports.is_empty() { | 88 | if proposed_imports.is_empty() { |
97 | return None; | 89 | return None; |
98 | } | 90 | } |
99 | 91 | ||
100 | let range = ctx.sema.original_range(import_assets.syntax_under_caret()).range; | 92 | let range = ctx.sema.original_range(&syntax_under_caret).range; |
101 | let group = import_group_message(import_assets.import_candidate()); | 93 | let group = import_group_message(import_assets.import_candidate()); |
102 | let scope = | 94 | let scope = ImportScope::find_insert_use_container(&syntax_under_caret, &ctx.sema)?; |
103 | ImportScope::find_insert_use_container(import_assets.syntax_under_caret(), &ctx.sema)?; | ||
104 | for (import, _) in proposed_imports { | 95 | for (import, _) in proposed_imports { |
105 | acc.add_group( | 96 | acc.add_group( |
106 | &group, | 97 | &group, |
@@ -117,14 +108,28 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
117 | Some(()) | 108 | Some(()) |
118 | } | 109 | } |
119 | 110 | ||
111 | pub(super) fn find_importable_node(ctx: &AssistContext) -> Option<(ImportAssets, SyntaxNode)> { | ||
112 | if let Some(path_under_caret) = ctx.find_node_at_offset_with_descend::<ast::Path>() { | ||
113 | ImportAssets::for_exact_path(&path_under_caret, &ctx.sema) | ||
114 | .zip(Some(path_under_caret.syntax().clone())) | ||
115 | } else if let Some(method_under_caret) = | ||
116 | ctx.find_node_at_offset_with_descend::<ast::MethodCallExpr>() | ||
117 | { | ||
118 | ImportAssets::for_method_call(&method_under_caret, &ctx.sema) | ||
119 | .zip(Some(method_under_caret.syntax().clone())) | ||
120 | } else { | ||
121 | None | ||
122 | } | ||
123 | } | ||
124 | |||
120 | fn import_group_message(import_candidate: &ImportCandidate) -> GroupLabel { | 125 | fn import_group_message(import_candidate: &ImportCandidate) -> GroupLabel { |
121 | let name = match import_candidate { | 126 | let name = match import_candidate { |
122 | ImportCandidate::Path(candidate) => format!("Import {}", &candidate.name), | 127 | ImportCandidate::Path(candidate) => format!("Import {}", candidate.name.text()), |
123 | ImportCandidate::TraitAssocItem(candidate) => { | 128 | ImportCandidate::TraitAssocItem(candidate) => { |
124 | format!("Import a trait for item {}", &candidate.name) | 129 | format!("Import a trait for item {}", candidate.name.text()) |
125 | } | 130 | } |
126 | ImportCandidate::TraitMethod(candidate) => { | 131 | ImportCandidate::TraitMethod(candidate) => { |
127 | format!("Import a trait for method {}", &candidate.name) | 132 | format!("Import a trait for method {}", candidate.name.text()) |
128 | } | 133 | } |
129 | }; | 134 | }; |
130 | GroupLabel(name) | 135 | GroupLabel(name) |
diff --git a/crates/assists/src/handlers/qualify_path.rs b/crates/assists/src/handlers/qualify_path.rs index a7d9fd4dc..af8a11d03 100644 --- a/crates/assists/src/handlers/qualify_path.rs +++ b/crates/assists/src/handlers/qualify_path.rs | |||
@@ -1,10 +1,7 @@ | |||
1 | use std::iter; | 1 | use std::iter; |
2 | 2 | ||
3 | use hir::AsName; | 3 | use hir::AsName; |
4 | use ide_db::helpers::{ | 4 | use ide_db::helpers::{import_assets::ImportCandidate, mod_path_to_ast}; |
5 | import_assets::{ImportAssets, ImportCandidate}, | ||
6 | mod_path_to_ast, | ||
7 | }; | ||
8 | use ide_db::RootDatabase; | 5 | use ide_db::RootDatabase; |
9 | use syntax::{ | 6 | use syntax::{ |
10 | ast, | 7 | ast, |
@@ -18,6 +15,8 @@ use crate::{ | |||
18 | AssistId, AssistKind, GroupLabel, | 15 | AssistId, AssistKind, GroupLabel, |
19 | }; | 16 | }; |
20 | 17 | ||
18 | use super::auto_import::find_importable_node; | ||
19 | |||
21 | // Assist: qualify_path | 20 | // Assist: qualify_path |
22 | // | 21 | // |
23 | // If the name is unresolved, provides all possible qualified paths for it. | 22 | // If the name is unresolved, provides all possible qualified paths for it. |
@@ -36,47 +35,38 @@ use crate::{ | |||
36 | // # pub mod std { pub mod collections { pub struct HashMap { } } } | 35 | // # pub mod std { pub mod collections { pub struct HashMap { } } } |
37 | // ``` | 36 | // ``` |
38 | pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 37 | pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
39 | let import_assets = | 38 | let (import_assets, syntax_under_caret) = find_importable_node(ctx)?; |
40 | if let Some(path_under_caret) = ctx.find_node_at_offset_with_descend::<ast::Path>() { | ||
41 | ImportAssets::for_regular_path(path_under_caret, &ctx.sema) | ||
42 | } else if let Some(method_under_caret) = | ||
43 | ctx.find_node_at_offset_with_descend::<ast::MethodCallExpr>() | ||
44 | { | ||
45 | ImportAssets::for_method_call(method_under_caret, &ctx.sema) | ||
46 | } else { | ||
47 | None | ||
48 | }?; | ||
49 | let proposed_imports = import_assets.search_for_relative_paths(&ctx.sema); | 39 | let proposed_imports = import_assets.search_for_relative_paths(&ctx.sema); |
50 | if proposed_imports.is_empty() { | 40 | if proposed_imports.is_empty() { |
51 | return None; | 41 | return None; |
52 | } | 42 | } |
53 | 43 | ||
54 | let candidate = import_assets.import_candidate(); | 44 | let candidate = import_assets.import_candidate(); |
55 | let range = ctx.sema.original_range(import_assets.syntax_under_caret()).range; | 45 | let range = ctx.sema.original_range(&syntax_under_caret).range; |
56 | 46 | ||
57 | let qualify_candidate = match candidate { | 47 | let qualify_candidate = match candidate { |
58 | ImportCandidate::Path(candidate) => { | 48 | ImportCandidate::Path(candidate) => { |
59 | if candidate.qualifier.is_some() { | 49 | if candidate.qualifier.is_some() { |
60 | mark::hit!(qualify_path_qualifier_start); | 50 | mark::hit!(qualify_path_qualifier_start); |
61 | let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?; | 51 | let path = ast::Path::cast(syntax_under_caret)?; |
62 | let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?); | 52 | let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?); |
63 | QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list()) | 53 | QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list()) |
64 | } else { | 54 | } else { |
65 | mark::hit!(qualify_path_unqualified_name); | 55 | mark::hit!(qualify_path_unqualified_name); |
66 | let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?; | 56 | let path = ast::Path::cast(syntax_under_caret)?; |
67 | let generics = path.segment()?.generic_arg_list(); | 57 | let generics = path.segment()?.generic_arg_list(); |
68 | QualifyCandidate::UnqualifiedName(generics) | 58 | QualifyCandidate::UnqualifiedName(generics) |
69 | } | 59 | } |
70 | } | 60 | } |
71 | ImportCandidate::TraitAssocItem(_) => { | 61 | ImportCandidate::TraitAssocItem(_) => { |
72 | mark::hit!(qualify_path_trait_assoc_item); | 62 | mark::hit!(qualify_path_trait_assoc_item); |
73 | let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?; | 63 | let path = ast::Path::cast(syntax_under_caret)?; |
74 | let (qualifier, segment) = (path.qualifier()?, path.segment()?); | 64 | let (qualifier, segment) = (path.qualifier()?, path.segment()?); |
75 | QualifyCandidate::TraitAssocItem(qualifier, segment) | 65 | QualifyCandidate::TraitAssocItem(qualifier, segment) |
76 | } | 66 | } |
77 | ImportCandidate::TraitMethod(_) => { | 67 | ImportCandidate::TraitMethod(_) => { |
78 | mark::hit!(qualify_path_trait_method); | 68 | mark::hit!(qualify_path_trait_method); |
79 | let mcall_expr = ast::MethodCallExpr::cast(import_assets.syntax_under_caret().clone())?; | 69 | let mcall_expr = ast::MethodCallExpr::cast(syntax_under_caret)?; |
80 | QualifyCandidate::TraitMethod(ctx.sema.db, mcall_expr) | 70 | QualifyCandidate::TraitMethod(ctx.sema.db, mcall_expr) |
81 | } | 71 | } |
82 | }; | 72 | }; |
@@ -140,7 +130,7 @@ impl QualifyCandidate<'_> { | |||
140 | let generics = | 130 | let generics = |
141 | mcall_expr.generic_arg_list().as_ref().map_or_else(String::new, ToString::to_string); | 131 | mcall_expr.generic_arg_list().as_ref().map_or_else(String::new, ToString::to_string); |
142 | let arg_list = mcall_expr.arg_list().map(|arg_list| arg_list.args()); | 132 | let arg_list = mcall_expr.arg_list().map(|arg_list| arg_list.args()); |
143 | let trait_ = item_as_trait(item)?; | 133 | let trait_ = item_as_trait(db, item)?; |
144 | let method = find_trait_method(db, trait_, &trait_method_name)?; | 134 | let method = find_trait_method(db, trait_, &trait_method_name)?; |
145 | if let Some(self_access) = method.self_param(db).map(|sp| sp.access(db)) { | 135 | if let Some(self_access) = method.self_param(db).map(|sp| sp.access(db)) { |
146 | let receiver = match self_access { | 136 | let receiver = match self_access { |
@@ -179,11 +169,13 @@ fn find_trait_method( | |||
179 | } | 169 | } |
180 | } | 170 | } |
181 | 171 | ||
182 | fn item_as_trait(item: hir::ItemInNs) -> Option<hir::Trait> { | 172 | fn item_as_trait(db: &RootDatabase, item: hir::ItemInNs) -> Option<hir::Trait> { |
183 | if let hir::ModuleDef::Trait(trait_) = hir::ModuleDef::from(item.as_module_def_id()?) { | 173 | let item_module_def = hir::ModuleDef::from(item.as_module_def_id()?); |
174 | |||
175 | if let hir::ModuleDef::Trait(trait_) = item_module_def { | ||
184 | Some(trait_) | 176 | Some(trait_) |
185 | } else { | 177 | } else { |
186 | None | 178 | item_module_def.as_assoc_item(db)?.containing_trait(db) |
187 | } | 179 | } |
188 | } | 180 | } |
189 | 181 | ||
@@ -191,7 +183,8 @@ fn group_label(candidate: &ImportCandidate) -> GroupLabel { | |||
191 | let name = match candidate { | 183 | let name = match candidate { |
192 | ImportCandidate::Path(it) => &it.name, | 184 | ImportCandidate::Path(it) => &it.name, |
193 | ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => &it.name, | 185 | ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => &it.name, |
194 | }; | 186 | } |
187 | .text(); | ||
195 | GroupLabel(format!("Qualify {}", name)) | 188 | GroupLabel(format!("Qualify {}", name)) |
196 | } | 189 | } |
197 | 190 | ||
diff --git a/crates/completion/src/completions/attribute.rs b/crates/completion/src/completions/attribute.rs index e5522980d..ab25a8c58 100644 --- a/crates/completion/src/completions/attribute.rs +++ b/crates/completion/src/completions/attribute.rs | |||
@@ -99,13 +99,14 @@ const ATTRIBUTES: &[AttrCompletion] = &[ | |||
99 | Some("export_name"), | 99 | Some("export_name"), |
100 | Some(r#"export_name = "${0:exported_symbol_name}""#), | 100 | Some(r#"export_name = "${0:exported_symbol_name}""#), |
101 | ), | 101 | ), |
102 | attr(r#"doc(alias = "…")"#, Some("docalias"), Some(r#"doc(alias = "${0:docs}")"#)), | ||
102 | attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)), | 103 | attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)), |
103 | attr("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(), | 104 | attr("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(), |
104 | attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")), | 105 | attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")), |
105 | // FIXME: resolve through macro resolution? | 106 | // FIXME: resolve through macro resolution? |
106 | attr("global_allocator", None, None).prefer_inner(), | 107 | attr("global_allocator", None, None).prefer_inner(), |
107 | attr(r#"ignore = "…""#, Some("ignore"), Some(r#"ignore = "${0:reason}""#)), | 108 | attr(r#"ignore = "…""#, Some("ignore"), Some(r#"ignore = "${0:reason}""#)), |
108 | attr("inline(…)", Some("inline"), Some("inline(${0:lint})")), | 109 | attr("inline", Some("inline"), Some("inline")), |
109 | attr("link", None, None), | 110 | attr("link", None, None), |
110 | attr(r#"link_name = "…""#, Some("link_name"), Some(r#"link_name = "${0:symbol_name}""#)), | 111 | attr(r#"link_name = "…""#, Some("link_name"), Some(r#"link_name = "${0:symbol_name}""#)), |
111 | attr( | 112 | attr( |
@@ -468,10 +469,11 @@ struct Test {} | |||
468 | at deprecated | 469 | at deprecated |
469 | at derive(…) | 470 | at derive(…) |
470 | at export_name = "…" | 471 | at export_name = "…" |
472 | at doc(alias = "…") | ||
471 | at doc = "…" | 473 | at doc = "…" |
472 | at forbid(…) | 474 | at forbid(…) |
473 | at ignore = "…" | 475 | at ignore = "…" |
474 | at inline(…) | 476 | at inline |
475 | at link | 477 | at link |
476 | at link_name = "…" | 478 | at link_name = "…" |
477 | at link_section = "…" | 479 | at link_section = "…" |
@@ -515,12 +517,13 @@ struct Test {} | |||
515 | at deprecated | 517 | at deprecated |
516 | at derive(…) | 518 | at derive(…) |
517 | at export_name = "…" | 519 | at export_name = "…" |
520 | at doc(alias = "…") | ||
518 | at doc = "…" | 521 | at doc = "…" |
519 | at feature(…) | 522 | at feature(…) |
520 | at forbid(…) | 523 | at forbid(…) |
521 | at global_allocator | 524 | at global_allocator |
522 | at ignore = "…" | 525 | at ignore = "…" |
523 | at inline(…) | 526 | at inline |
524 | at link | 527 | at link |
525 | at link_name = "…" | 528 | at link_name = "…" |
526 | at link_section = "…" | 529 | at link_section = "…" |
diff --git a/crates/completion/src/completions/flyimport.rs b/crates/completion/src/completions/flyimport.rs index 222809638..47e797ac8 100644 --- a/crates/completion/src/completions/flyimport.rs +++ b/crates/completion/src/completions/flyimport.rs | |||
@@ -20,11 +20,14 @@ | |||
20 | //! # pub mod std { pub mod marker { pub struct PhantomData { } } } | 20 | //! # pub mod std { pub mod marker { pub struct PhantomData { } } } |
21 | //! ``` | 21 | //! ``` |
22 | //! | 22 | //! |
23 | //! Also completes associated items, that require trait imports. | ||
24 | //! | ||
23 | //! .Fuzzy search details | 25 | //! .Fuzzy search details |
24 | //! | 26 | //! |
25 | //! To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only | 27 | //! To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only |
26 | //! (i.e. in `HashMap` in the `std::collections::HashMap` path). | 28 | //! (i.e. in `HashMap` in the `std::collections::HashMap` path). |
27 | //! For the same reasons, avoids searching for any imports for inputs with their length less that 2 symbols. | 29 | //! For the same reasons, avoids searching for any path imports for inputs with their length less that 2 symbols |
30 | //! (but shows all associated items for any input length). | ||
28 | //! | 31 | //! |
29 | //! .Import configuration | 32 | //! .Import configuration |
30 | //! | 33 | //! |
@@ -45,10 +48,12 @@ | |||
45 | //! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding | 48 | //! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding |
46 | //! capability enabled. | 49 | //! capability enabled. |
47 | 50 | ||
48 | use either::Either; | ||
49 | use hir::{ModPath, ScopeDef}; | 51 | use hir::{ModPath, ScopeDef}; |
50 | use ide_db::{helpers::insert_use::ImportScope, imports_locator}; | 52 | use ide_db::helpers::{ |
51 | use syntax::AstNode; | 53 | import_assets::{ImportAssets, ImportCandidate}, |
54 | insert_use::ImportScope, | ||
55 | }; | ||
56 | use syntax::{AstNode, SyntaxNode, T}; | ||
52 | use test_utils::mark; | 57 | use test_utils::mark; |
53 | 58 | ||
54 | use crate::{ | 59 | use crate::{ |
@@ -60,58 +65,108 @@ use crate::{ | |||
60 | use super::Completions; | 65 | use super::Completions; |
61 | 66 | ||
62 | pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { | 67 | pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { |
63 | if !ctx.config.enable_autoimport_completions { | 68 | if !ctx.config.enable_imports_on_the_fly { |
64 | return None; | 69 | return None; |
65 | } | 70 | } |
66 | if ctx.attribute_under_caret.is_some() || ctx.mod_declaration_under_caret.is_some() { | 71 | if ctx.attribute_under_caret.is_some() || ctx.mod_declaration_under_caret.is_some() { |
67 | return None; | 72 | return None; |
68 | } | 73 | } |
69 | let potential_import_name = ctx.token.to_string(); | 74 | let potential_import_name = { |
70 | if potential_import_name.len() < 2 { | 75 | let token_kind = ctx.token.kind(); |
71 | return None; | 76 | if matches!(token_kind, T![.] | T![::]) { |
72 | } | 77 | String::new() |
73 | let _p = profile::span("import_on_the_fly").detail(|| potential_import_name.to_string()); | 78 | } else { |
79 | ctx.token.to_string() | ||
80 | } | ||
81 | }; | ||
74 | 82 | ||
75 | let current_module = ctx.scope.module()?; | 83 | let _p = profile::span("import_on_the_fly").detail(|| potential_import_name.to_string()); |
76 | let anchor = ctx.name_ref_syntax.as_ref()?; | ||
77 | let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?; | ||
78 | 84 | ||
79 | let user_input_lowercased = potential_import_name.to_lowercase(); | 85 | let user_input_lowercased = potential_import_name.to_lowercase(); |
80 | let mut all_mod_paths = imports_locator::find_similar_imports( | 86 | let import_assets = import_assets(ctx, potential_import_name)?; |
87 | let import_scope = ImportScope::find_insert_use_container( | ||
88 | position_for_import(ctx, Some(import_assets.import_candidate()))?, | ||
81 | &ctx.sema, | 89 | &ctx.sema, |
82 | ctx.krate?, | 90 | )?; |
83 | Some(40), | 91 | let mut all_mod_paths = import_assets |
84 | potential_import_name, | 92 | .search_for_relative_paths(&ctx.sema) |
85 | true, | 93 | .into_iter() |
86 | true, | 94 | .map(|(mod_path, item_in_ns)| { |
87 | ) | 95 | let scope_item = match item_in_ns { |
88 | .filter_map(|import_candidate| { | 96 | hir::ItemInNs::Types(id) => ScopeDef::ModuleDef(id.into()), |
89 | Some(match import_candidate { | 97 | hir::ItemInNs::Values(id) => ScopeDef::ModuleDef(id.into()), |
90 | Either::Left(module_def) => { | 98 | hir::ItemInNs::Macros(id) => ScopeDef::MacroDef(id.into()), |
91 | (current_module.find_use_path(ctx.db, module_def)?, ScopeDef::ModuleDef(module_def)) | 99 | }; |
92 | } | 100 | (mod_path, scope_item) |
93 | Either::Right(macro_def) => { | ||
94 | (current_module.find_use_path(ctx.db, macro_def)?, ScopeDef::MacroDef(macro_def)) | ||
95 | } | ||
96 | }) | 101 | }) |
97 | }) | 102 | .collect::<Vec<_>>(); |
98 | .filter(|(mod_path, _)| mod_path.len() > 1) | ||
99 | .collect::<Vec<_>>(); | ||
100 | |||
101 | all_mod_paths.sort_by_cached_key(|(mod_path, _)| { | 103 | all_mod_paths.sort_by_cached_key(|(mod_path, _)| { |
102 | compute_fuzzy_completion_order_key(mod_path, &user_input_lowercased) | 104 | compute_fuzzy_completion_order_key(mod_path, &user_input_lowercased) |
103 | }); | 105 | }); |
104 | 106 | ||
105 | acc.add_all(all_mod_paths.into_iter().filter_map(|(import_path, definition)| { | 107 | acc.add_all(all_mod_paths.into_iter().filter_map(|(import_path, definition)| { |
106 | render_resolution_with_import( | 108 | let import_for_trait_assoc_item = match definition { |
107 | RenderContext::new(ctx), | 109 | ScopeDef::ModuleDef(module_def) => module_def |
108 | ImportEdit { import_path, import_scope: import_scope.clone() }, | 110 | .as_assoc_item(ctx.db) |
109 | &definition, | 111 | .and_then(|assoc| assoc.containing_trait(ctx.db)) |
110 | ) | 112 | .is_some(), |
113 | _ => false, | ||
114 | }; | ||
115 | let import_edit = ImportEdit { | ||
116 | import_path, | ||
117 | import_scope: import_scope.clone(), | ||
118 | import_for_trait_assoc_item, | ||
119 | }; | ||
120 | render_resolution_with_import(RenderContext::new(ctx), import_edit, &definition) | ||
111 | })); | 121 | })); |
112 | Some(()) | 122 | Some(()) |
113 | } | 123 | } |
114 | 124 | ||
125 | pub(crate) fn position_for_import<'a>( | ||
126 | ctx: &'a CompletionContext, | ||
127 | import_candidate: Option<&ImportCandidate>, | ||
128 | ) -> Option<&'a SyntaxNode> { | ||
129 | Some(match import_candidate { | ||
130 | Some(ImportCandidate::Path(_)) => ctx.name_ref_syntax.as_ref()?.syntax(), | ||
131 | Some(ImportCandidate::TraitAssocItem(_)) => ctx.path_qual.as_ref()?.syntax(), | ||
132 | Some(ImportCandidate::TraitMethod(_)) => ctx.dot_receiver.as_ref()?.syntax(), | ||
133 | None => ctx | ||
134 | .name_ref_syntax | ||
135 | .as_ref() | ||
136 | .map(|name_ref| name_ref.syntax()) | ||
137 | .or_else(|| ctx.path_qual.as_ref().map(|path| path.syntax())) | ||
138 | .or_else(|| ctx.dot_receiver.as_ref().map(|expr| expr.syntax()))?, | ||
139 | }) | ||
140 | } | ||
141 | |||
142 | fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAssets> { | ||
143 | let current_module = ctx.scope.module()?; | ||
144 | if let Some(dot_receiver) = &ctx.dot_receiver { | ||
145 | ImportAssets::for_fuzzy_method_call( | ||
146 | current_module, | ||
147 | ctx.sema.type_of_expr(dot_receiver)?, | ||
148 | fuzzy_name, | ||
149 | ) | ||
150 | } else { | ||
151 | let fuzzy_name_length = fuzzy_name.len(); | ||
152 | let assets_for_path = ImportAssets::for_fuzzy_path( | ||
153 | current_module, | ||
154 | ctx.path_qual.clone(), | ||
155 | fuzzy_name, | ||
156 | &ctx.sema, | ||
157 | ); | ||
158 | |||
159 | if matches!(assets_for_path.as_ref()?.import_candidate(), ImportCandidate::Path(_)) | ||
160 | && fuzzy_name_length < 2 | ||
161 | { | ||
162 | mark::hit!(ignore_short_input_for_path); | ||
163 | None | ||
164 | } else { | ||
165 | assets_for_path | ||
166 | } | ||
167 | } | ||
168 | } | ||
169 | |||
115 | fn compute_fuzzy_completion_order_key( | 170 | fn compute_fuzzy_completion_order_key( |
116 | proposed_mod_path: &ModPath, | 171 | proposed_mod_path: &ModPath, |
117 | user_input_lowercased: &str, | 172 | user_input_lowercased: &str, |
@@ -224,6 +279,30 @@ fn main() { | |||
224 | } | 279 | } |
225 | 280 | ||
226 | #[test] | 281 | #[test] |
282 | fn short_paths_are_ignored() { | ||
283 | mark::check!(ignore_short_input_for_path); | ||
284 | |||
285 | check( | ||
286 | r#" | ||
287 | //- /lib.rs crate:dep | ||
288 | pub struct FirstStruct; | ||
289 | pub mod some_module { | ||
290 | pub struct SecondStruct; | ||
291 | pub struct ThirdStruct; | ||
292 | } | ||
293 | |||
294 | //- /main.rs crate:main deps:dep | ||
295 | use dep::{FirstStruct, some_module::SecondStruct}; | ||
296 | |||
297 | fn main() { | ||
298 | t$0 | ||
299 | } | ||
300 | "#, | ||
301 | expect![[r#""#]], | ||
302 | ); | ||
303 | } | ||
304 | |||
305 | #[test] | ||
227 | fn fuzzy_completions_come_in_specific_order() { | 306 | fn fuzzy_completions_come_in_specific_order() { |
228 | mark::check!(certain_fuzzy_order_test); | 307 | mark::check!(certain_fuzzy_order_test); |
229 | check( | 308 | check( |
@@ -259,6 +338,176 @@ fn main() { | |||
259 | } | 338 | } |
260 | 339 | ||
261 | #[test] | 340 | #[test] |
341 | fn trait_function_fuzzy_completion() { | ||
342 | let fixture = r#" | ||
343 | //- /lib.rs crate:dep | ||
344 | pub mod test_mod { | ||
345 | pub trait TestTrait { | ||
346 | const SPECIAL_CONST: u8; | ||
347 | type HumbleType; | ||
348 | fn weird_function(); | ||
349 | fn random_method(&self); | ||
350 | } | ||
351 | pub struct TestStruct {} | ||
352 | impl TestTrait for TestStruct { | ||
353 | const SPECIAL_CONST: u8 = 42; | ||
354 | type HumbleType = (); | ||
355 | fn weird_function() {} | ||
356 | fn random_method(&self) {} | ||
357 | } | ||
358 | } | ||
359 | |||
360 | //- /main.rs crate:main deps:dep | ||
361 | fn main() { | ||
362 | dep::test_mod::TestStruct::wei$0 | ||
363 | } | ||
364 | "#; | ||
365 | |||
366 | check( | ||
367 | fixture, | ||
368 | expect![[r#" | ||
369 | fn weird_function() (dep::test_mod::TestTrait) fn weird_function() | ||
370 | "#]], | ||
371 | ); | ||
372 | |||
373 | check_edit( | ||
374 | "weird_function", | ||
375 | fixture, | ||
376 | r#" | ||
377 | use dep::test_mod::TestTrait; | ||
378 | |||
379 | fn main() { | ||
380 | dep::test_mod::TestStruct::weird_function()$0 | ||
381 | } | ||
382 | "#, | ||
383 | ); | ||
384 | } | ||
385 | |||
386 | #[test] | ||
387 | fn trait_const_fuzzy_completion() { | ||
388 | let fixture = r#" | ||
389 | //- /lib.rs crate:dep | ||
390 | pub mod test_mod { | ||
391 | pub trait TestTrait { | ||
392 | const SPECIAL_CONST: u8; | ||
393 | type HumbleType; | ||
394 | fn weird_function(); | ||
395 | fn random_method(&self); | ||
396 | } | ||
397 | pub struct TestStruct {} | ||
398 | impl TestTrait for TestStruct { | ||
399 | const SPECIAL_CONST: u8 = 42; | ||
400 | type HumbleType = (); | ||
401 | fn weird_function() {} | ||
402 | fn random_method(&self) {} | ||
403 | } | ||
404 | } | ||
405 | |||
406 | //- /main.rs crate:main deps:dep | ||
407 | fn main() { | ||
408 | dep::test_mod::TestStruct::spe$0 | ||
409 | } | ||
410 | "#; | ||
411 | |||
412 | check( | ||
413 | fixture, | ||
414 | expect![[r#" | ||
415 | ct SPECIAL_CONST (dep::test_mod::TestTrait) | ||
416 | "#]], | ||
417 | ); | ||
418 | |||
419 | check_edit( | ||
420 | "SPECIAL_CONST", | ||
421 | fixture, | ||
422 | r#" | ||
423 | use dep::test_mod::TestTrait; | ||
424 | |||
425 | fn main() { | ||
426 | dep::test_mod::TestStruct::SPECIAL_CONST | ||
427 | } | ||
428 | "#, | ||
429 | ); | ||
430 | } | ||
431 | |||
432 | #[test] | ||
433 | fn trait_method_fuzzy_completion() { | ||
434 | let fixture = r#" | ||
435 | //- /lib.rs crate:dep | ||
436 | pub mod test_mod { | ||
437 | pub trait TestTrait { | ||
438 | const SPECIAL_CONST: u8; | ||
439 | type HumbleType; | ||
440 | fn weird_function(); | ||
441 | fn random_method(&self); | ||
442 | } | ||
443 | pub struct TestStruct {} | ||
444 | impl TestTrait for TestStruct { | ||
445 | const SPECIAL_CONST: u8 = 42; | ||
446 | type HumbleType = (); | ||
447 | fn weird_function() {} | ||
448 | fn random_method(&self) {} | ||
449 | } | ||
450 | } | ||
451 | |||
452 | //- /main.rs crate:main deps:dep | ||
453 | fn main() { | ||
454 | let test_struct = dep::test_mod::TestStruct {}; | ||
455 | test_struct.ran$0 | ||
456 | } | ||
457 | "#; | ||
458 | |||
459 | check( | ||
460 | fixture, | ||
461 | expect![[r#" | ||
462 | me random_method() (dep::test_mod::TestTrait) fn random_method(&self) | ||
463 | "#]], | ||
464 | ); | ||
465 | |||
466 | check_edit( | ||
467 | "random_method", | ||
468 | fixture, | ||
469 | r#" | ||
470 | use dep::test_mod::TestTrait; | ||
471 | |||
472 | fn main() { | ||
473 | let test_struct = dep::test_mod::TestStruct {}; | ||
474 | test_struct.random_method()$0 | ||
475 | } | ||
476 | "#, | ||
477 | ); | ||
478 | } | ||
479 | |||
480 | #[test] | ||
481 | fn no_trait_type_fuzzy_completion() { | ||
482 | check( | ||
483 | r#" | ||
484 | //- /lib.rs crate:dep | ||
485 | pub mod test_mod { | ||
486 | pub trait TestTrait { | ||
487 | const SPECIAL_CONST: u8; | ||
488 | type HumbleType; | ||
489 | fn weird_function(); | ||
490 | fn random_method(&self); | ||
491 | } | ||
492 | pub struct TestStruct {} | ||
493 | impl TestTrait for TestStruct { | ||
494 | const SPECIAL_CONST: u8 = 42; | ||
495 | type HumbleType = (); | ||
496 | fn weird_function() {} | ||
497 | fn random_method(&self) {} | ||
498 | } | ||
499 | } | ||
500 | |||
501 | //- /main.rs crate:main deps:dep | ||
502 | fn main() { | ||
503 | dep::test_mod::TestStruct::hum$0 | ||
504 | } | ||
505 | "#, | ||
506 | expect![[r#""#]], | ||
507 | ); | ||
508 | } | ||
509 | |||
510 | #[test] | ||
262 | fn does_not_propose_names_in_scope() { | 511 | fn does_not_propose_names_in_scope() { |
263 | check( | 512 | check( |
264 | r#" | 513 | r#" |
@@ -288,4 +537,129 @@ fn main() { | |||
288 | expect![[r#""#]], | 537 | expect![[r#""#]], |
289 | ); | 538 | ); |
290 | } | 539 | } |
540 | |||
541 | #[test] | ||
542 | fn does_not_propose_traits_in_scope() { | ||
543 | check( | ||
544 | r#" | ||
545 | //- /lib.rs crate:dep | ||
546 | pub mod test_mod { | ||
547 | pub trait TestTrait { | ||
548 | const SPECIAL_CONST: u8; | ||
549 | type HumbleType; | ||
550 | fn weird_function(); | ||
551 | fn random_method(&self); | ||
552 | } | ||
553 | pub struct TestStruct {} | ||
554 | impl TestTrait for TestStruct { | ||
555 | const SPECIAL_CONST: u8 = 42; | ||
556 | type HumbleType = (); | ||
557 | fn weird_function() {} | ||
558 | fn random_method(&self) {} | ||
559 | } | ||
560 | } | ||
561 | |||
562 | //- /main.rs crate:main deps:dep | ||
563 | use dep::test_mod::{TestStruct, TestTrait}; | ||
564 | fn main() { | ||
565 | dep::test_mod::TestStruct::hum$0 | ||
566 | } | ||
567 | "#, | ||
568 | expect![[r#""#]], | ||
569 | ); | ||
570 | } | ||
571 | |||
572 | #[test] | ||
573 | fn blanket_trait_impl_import() { | ||
574 | check_edit( | ||
575 | "another_function", | ||
576 | r#" | ||
577 | //- /lib.rs crate:dep | ||
578 | pub mod test_mod { | ||
579 | pub struct TestStruct {} | ||
580 | pub trait TestTrait { | ||
581 | fn another_function(); | ||
582 | } | ||
583 | impl<T> TestTrait for T { | ||
584 | fn another_function() {} | ||
585 | } | ||
586 | } | ||
587 | |||
588 | //- /main.rs crate:main deps:dep | ||
589 | fn main() { | ||
590 | dep::test_mod::TestStruct::ano$0 | ||
591 | } | ||
592 | "#, | ||
593 | r#" | ||
594 | use dep::test_mod::TestTrait; | ||
595 | |||
596 | fn main() { | ||
597 | dep::test_mod::TestStruct::another_function()$0 | ||
598 | } | ||
599 | "#, | ||
600 | ); | ||
601 | } | ||
602 | |||
603 | #[test] | ||
604 | fn zero_input_assoc_item_completion() { | ||
605 | check( | ||
606 | r#" | ||
607 | //- /lib.rs crate:dep | ||
608 | pub mod test_mod { | ||
609 | pub trait TestTrait { | ||
610 | const SPECIAL_CONST: u8; | ||
611 | type HumbleType; | ||
612 | fn weird_function(); | ||
613 | fn random_method(&self); | ||
614 | } | ||
615 | pub struct TestStruct {} | ||
616 | impl TestTrait for TestStruct { | ||
617 | const SPECIAL_CONST: u8 = 42; | ||
618 | type HumbleType = (); | ||
619 | fn weird_function() {} | ||
620 | fn random_method(&self) {} | ||
621 | } | ||
622 | } | ||
623 | |||
624 | //- /main.rs crate:main deps:dep | ||
625 | fn main() { | ||
626 | let test_struct = dep::test_mod::TestStruct {}; | ||
627 | test_struct.$0 | ||
628 | } | ||
629 | "#, | ||
630 | expect![[r#" | ||
631 | me random_method() (dep::test_mod::TestTrait) fn random_method(&self) | ||
632 | "#]], | ||
633 | ); | ||
634 | |||
635 | check( | ||
636 | r#" | ||
637 | //- /lib.rs crate:dep | ||
638 | pub mod test_mod { | ||
639 | pub trait TestTrait { | ||
640 | const SPECIAL_CONST: u8; | ||
641 | type HumbleType; | ||
642 | fn weird_function(); | ||
643 | fn random_method(&self); | ||
644 | } | ||
645 | pub struct TestStruct {} | ||
646 | impl TestTrait for TestStruct { | ||
647 | const SPECIAL_CONST: u8 = 42; | ||
648 | type HumbleType = (); | ||
649 | fn weird_function() {} | ||
650 | fn random_method(&self) {} | ||
651 | } | ||
652 | } | ||
653 | |||
654 | //- /main.rs crate:main deps:dep | ||
655 | fn main() { | ||
656 | dep::test_mod::TestStruct::$0 | ||
657 | } | ||
658 | "#, | ||
659 | expect![[r#" | ||
660 | ct SPECIAL_CONST (dep::test_mod::TestTrait) | ||
661 | fn weird_function() (dep::test_mod::TestTrait) fn weird_function() | ||
662 | "#]], | ||
663 | ); | ||
664 | } | ||
291 | } | 665 | } |
diff --git a/crates/completion/src/config.rs b/crates/completion/src/config.rs index 58fc700f3..d70ed6c1c 100644 --- a/crates/completion/src/config.rs +++ b/crates/completion/src/config.rs | |||
@@ -9,7 +9,7 @@ use ide_db::helpers::{insert_use::InsertUseConfig, SnippetCap}; | |||
9 | #[derive(Clone, Debug, PartialEq, Eq)] | 9 | #[derive(Clone, Debug, PartialEq, Eq)] |
10 | pub struct CompletionConfig { | 10 | pub struct CompletionConfig { |
11 | pub enable_postfix_completions: bool, | 11 | pub enable_postfix_completions: bool, |
12 | pub enable_autoimport_completions: bool, | 12 | pub enable_imports_on_the_fly: bool, |
13 | pub add_call_parenthesis: bool, | 13 | pub add_call_parenthesis: bool, |
14 | pub add_call_argument_snippets: bool, | 14 | pub add_call_argument_snippets: bool, |
15 | pub snippet_cap: Option<SnippetCap>, | 15 | pub snippet_cap: Option<SnippetCap>, |
diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs index 5d91d3a5c..4147853e7 100644 --- a/crates/completion/src/item.rs +++ b/crates/completion/src/item.rs | |||
@@ -270,6 +270,7 @@ impl CompletionItem { | |||
270 | pub struct ImportEdit { | 270 | pub struct ImportEdit { |
271 | pub import_path: ModPath, | 271 | pub import_path: ModPath, |
272 | pub import_scope: ImportScope, | 272 | pub import_scope: ImportScope, |
273 | pub import_for_trait_assoc_item: bool, | ||
273 | } | 274 | } |
274 | 275 | ||
275 | impl ImportEdit { | 276 | impl ImportEdit { |
@@ -321,17 +322,19 @@ impl Builder { | |||
321 | let mut insert_text = self.insert_text; | 322 | let mut insert_text = self.insert_text; |
322 | 323 | ||
323 | if let Some(import_to_add) = self.import_to_add.as_ref() { | 324 | if let Some(import_to_add) = self.import_to_add.as_ref() { |
324 | let mut import_path_without_last_segment = import_to_add.import_path.to_owned(); | 325 | if import_to_add.import_for_trait_assoc_item { |
325 | let _ = import_path_without_last_segment.segments.pop(); | 326 | lookup = lookup.or_else(|| Some(label.clone())); |
326 | 327 | insert_text = insert_text.or_else(|| Some(label.clone())); | |
327 | if !import_path_without_last_segment.segments.is_empty() { | 328 | label = format!("{} ({})", label, import_to_add.import_path); |
328 | if lookup.is_none() { | 329 | } else { |
329 | lookup = Some(label.clone()); | 330 | let mut import_path_without_last_segment = import_to_add.import_path.to_owned(); |
330 | } | 331 | let _ = import_path_without_last_segment.segments.pop(); |
331 | if insert_text.is_none() { | 332 | |
332 | insert_text = Some(label.clone()); | 333 | if !import_path_without_last_segment.segments.is_empty() { |
334 | lookup = lookup.or_else(|| Some(label.clone())); | ||
335 | insert_text = insert_text.or_else(|| Some(label.clone())); | ||
336 | label = format!("{}::{}", import_path_without_last_segment, label); | ||
333 | } | 337 | } |
334 | label = format!("{}::{}", import_path_without_last_segment, label); | ||
335 | } | 338 | } |
336 | } | 339 | } |
337 | 340 | ||
diff --git a/crates/completion/src/lib.rs b/crates/completion/src/lib.rs index ee1b822e7..2c4e54524 100644 --- a/crates/completion/src/lib.rs +++ b/crates/completion/src/lib.rs | |||
@@ -11,10 +11,10 @@ mod render; | |||
11 | 11 | ||
12 | mod completions; | 12 | mod completions; |
13 | 13 | ||
14 | use completions::flyimport::position_for_import; | ||
14 | use ide_db::{ | 15 | use ide_db::{ |
15 | base_db::FilePosition, helpers::insert_use::ImportScope, imports_locator, RootDatabase, | 16 | base_db::FilePosition, helpers::insert_use::ImportScope, imports_locator, RootDatabase, |
16 | }; | 17 | }; |
17 | use syntax::AstNode; | ||
18 | use text_edit::TextEdit; | 18 | use text_edit::TextEdit; |
19 | 19 | ||
20 | use crate::{completions::Completions, context::CompletionContext, item::CompletionKind}; | 20 | use crate::{completions::Completions, context::CompletionContext, item::CompletionKind}; |
@@ -139,12 +139,13 @@ pub fn resolve_completion_edits( | |||
139 | position: FilePosition, | 139 | position: FilePosition, |
140 | full_import_path: &str, | 140 | full_import_path: &str, |
141 | imported_name: String, | 141 | imported_name: String, |
142 | import_for_trait_assoc_item: bool, | ||
142 | ) -> Option<Vec<TextEdit>> { | 143 | ) -> Option<Vec<TextEdit>> { |
143 | let ctx = CompletionContext::new(db, position, config)?; | 144 | let ctx = CompletionContext::new(db, position, config)?; |
144 | let anchor = ctx.name_ref_syntax.as_ref()?; | 145 | let position_for_import = position_for_import(&ctx, None)?; |
145 | let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?; | 146 | let import_scope = ImportScope::find_insert_use_container(position_for_import, &ctx.sema)?; |
146 | 147 | ||
147 | let current_module = ctx.sema.scope(anchor.syntax()).module()?; | 148 | let current_module = ctx.sema.scope(position_for_import).module()?; |
148 | let current_crate = current_module.krate(); | 149 | let current_crate = current_module.krate(); |
149 | 150 | ||
150 | let import_path = imports_locator::find_exact_imports(&ctx.sema, current_crate, imported_name) | 151 | let import_path = imports_locator::find_exact_imports(&ctx.sema, current_crate, imported_name) |
@@ -154,7 +155,7 @@ pub fn resolve_completion_edits( | |||
154 | }) | 155 | }) |
155 | .find(|mod_path| mod_path.to_string() == full_import_path)?; | 156 | .find(|mod_path| mod_path.to_string() == full_import_path)?; |
156 | 157 | ||
157 | ImportEdit { import_path, import_scope } | 158 | ImportEdit { import_path, import_scope, import_for_trait_assoc_item } |
158 | .to_text_edit(config.insert_use.merge) | 159 | .to_text_edit(config.insert_use.merge) |
159 | .map(|edit| vec![edit]) | 160 | .map(|edit| vec![edit]) |
160 | } | 161 | } |
diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs index 820dd01d1..4b3c9702a 100644 --- a/crates/completion/src/render.rs +++ b/crates/completion/src/render.rs | |||
@@ -10,7 +10,7 @@ pub(crate) mod type_alias; | |||
10 | 10 | ||
11 | mod builder_ext; | 11 | mod builder_ext; |
12 | 12 | ||
13 | use hir::{Documentation, HasAttrs, HirDisplay, Mutability, ScopeDef, Type}; | 13 | use hir::{Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type}; |
14 | use ide_db::{helpers::SnippetCap, RootDatabase}; | 14 | use ide_db::{helpers::SnippetCap, RootDatabase}; |
15 | use syntax::TextRange; | 15 | use syntax::TextRange; |
16 | use test_utils::mark; | 16 | use test_utils::mark; |
@@ -51,16 +51,16 @@ pub(crate) fn render_resolution_with_import<'a>( | |||
51 | import_edit: ImportEdit, | 51 | import_edit: ImportEdit, |
52 | resolution: &ScopeDef, | 52 | resolution: &ScopeDef, |
53 | ) -> Option<CompletionItem> { | 53 | ) -> Option<CompletionItem> { |
54 | Render::new(ctx) | 54 | let local_name = match resolution { |
55 | .render_resolution( | 55 | ScopeDef::ModuleDef(ModuleDef::Function(f)) => f.name(ctx.completion.db).to_string(), |
56 | import_edit.import_path.segments.last()?.to_string(), | 56 | ScopeDef::ModuleDef(ModuleDef::Const(c)) => c.name(ctx.completion.db)?.to_string(), |
57 | Some(import_edit), | 57 | ScopeDef::ModuleDef(ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db).to_string(), |
58 | resolution, | 58 | _ => import_edit.import_path.segments.last()?.to_string(), |
59 | ) | 59 | }; |
60 | .map(|mut item| { | 60 | Render::new(ctx).render_resolution(local_name, Some(import_edit), resolution).map(|mut item| { |
61 | item.completion_kind = CompletionKind::Magic; | 61 | item.completion_kind = CompletionKind::Magic; |
62 | item | 62 | item |
63 | }) | 63 | }) |
64 | } | 64 | } |
65 | 65 | ||
66 | /// Interface for data and methods required for items rendering. | 66 | /// Interface for data and methods required for items rendering. |
diff --git a/crates/completion/src/test_utils.rs b/crates/completion/src/test_utils.rs index 6ea6da989..3faf861b9 100644 --- a/crates/completion/src/test_utils.rs +++ b/crates/completion/src/test_utils.rs | |||
@@ -18,7 +18,7 @@ use crate::{item::CompletionKind, CompletionConfig, CompletionItem}; | |||
18 | 18 | ||
19 | pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig { | 19 | pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig { |
20 | enable_postfix_completions: true, | 20 | enable_postfix_completions: true, |
21 | enable_autoimport_completions: true, | 21 | enable_imports_on_the_fly: true, |
22 | add_call_parenthesis: true, | 22 | add_call_parenthesis: true, |
23 | add_call_argument_snippets: true, | 23 | add_call_argument_snippets: true, |
24 | snippet_cap: SnippetCap::new(true), | 24 | snippet_cap: SnippetCap::new(true), |
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs index 6cbf5cecf..2950f08b8 100644 --- a/crates/hir/src/code_model.rs +++ b/crates/hir/src/code_model.rs | |||
@@ -272,6 +272,15 @@ impl ModuleDef { | |||
272 | 272 | ||
273 | hir_ty::diagnostics::validate_module_item(db, module.id.krate, id, sink) | 273 | hir_ty::diagnostics::validate_module_item(db, module.id.krate, id, sink) |
274 | } | 274 | } |
275 | |||
276 | pub fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> { | ||
277 | match self { | ||
278 | ModuleDef::Function(f) => f.as_assoc_item(db), | ||
279 | ModuleDef::Const(c) => c.as_assoc_item(db), | ||
280 | ModuleDef::TypeAlias(t) => t.as_assoc_item(db), | ||
281 | _ => None, | ||
282 | } | ||
283 | } | ||
275 | } | 284 | } |
276 | 285 | ||
277 | impl Module { | 286 | impl Module { |
@@ -1091,6 +1100,13 @@ impl AssocItem { | |||
1091 | AssocContainerId::ContainerId(_) => panic!("invalid AssocItem"), | 1100 | AssocContainerId::ContainerId(_) => panic!("invalid AssocItem"), |
1092 | } | 1101 | } |
1093 | } | 1102 | } |
1103 | |||
1104 | pub fn containing_trait(self, db: &dyn HirDatabase) -> Option<Trait> { | ||
1105 | match self.container(db) { | ||
1106 | AssocItemContainer::Trait(t) => Some(t), | ||
1107 | _ => None, | ||
1108 | } | ||
1109 | } | ||
1094 | } | 1110 | } |
1095 | 1111 | ||
1096 | impl HasVisibility for AssocItem { | 1112 | impl HasVisibility for AssocItem { |
diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs index 344f0b6c0..3b2dd0f6e 100644 --- a/crates/hir_def/src/body.rs +++ b/crates/hir_def/src/body.rs | |||
@@ -29,7 +29,7 @@ use crate::{ | |||
29 | expr::{Expr, ExprId, Label, LabelId, Pat, PatId}, | 29 | expr::{Expr, ExprId, Label, LabelId, Pat, PatId}, |
30 | item_scope::BuiltinShadowMode, | 30 | item_scope::BuiltinShadowMode, |
31 | item_scope::ItemScope, | 31 | item_scope::ItemScope, |
32 | nameres::CrateDefMap, | 32 | nameres::DefMap, |
33 | path::{ModPath, Path}, | 33 | path::{ModPath, Path}, |
34 | src::HasSource, | 34 | src::HasSource, |
35 | AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId, | 35 | AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId, |
@@ -45,7 +45,7 @@ pub(crate) struct CfgExpander { | |||
45 | 45 | ||
46 | pub(crate) struct Expander { | 46 | pub(crate) struct Expander { |
47 | cfg_expander: CfgExpander, | 47 | cfg_expander: CfgExpander, |
48 | crate_def_map: Arc<CrateDefMap>, | 48 | crate_def_map: Arc<DefMap>, |
49 | current_file_id: HirFileId, | 49 | current_file_id: HirFileId, |
50 | ast_id_map: Arc<AstIdMap>, | 50 | ast_id_map: Arc<AstIdMap>, |
51 | module: ModuleId, | 51 | module: ModuleId, |
diff --git a/crates/hir_def/src/db.rs b/crates/hir_def/src/db.rs index 6ef9fe790..91c8d45cd 100644 --- a/crates/hir_def/src/db.rs +++ b/crates/hir_def/src/db.rs | |||
@@ -15,7 +15,7 @@ use crate::{ | |||
15 | import_map::ImportMap, | 15 | import_map::ImportMap, |
16 | item_tree::ItemTree, | 16 | item_tree::ItemTree, |
17 | lang_item::{LangItemTarget, LangItems}, | 17 | lang_item::{LangItemTarget, LangItems}, |
18 | nameres::CrateDefMap, | 18 | nameres::DefMap, |
19 | AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc, | 19 | AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc, |
20 | GenericDefId, ImplId, ImplLoc, LocalEnumVariantId, LocalFieldId, StaticId, StaticLoc, StructId, | 20 | GenericDefId, ImplId, ImplLoc, LocalEnumVariantId, LocalFieldId, StaticId, StaticLoc, StructId, |
21 | StructLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, VariantId, | 21 | StructLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, VariantId, |
@@ -50,10 +50,10 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { | |||
50 | 50 | ||
51 | #[salsa::invoke(crate_def_map_wait)] | 51 | #[salsa::invoke(crate_def_map_wait)] |
52 | #[salsa::transparent] | 52 | #[salsa::transparent] |
53 | fn crate_def_map(&self, krate: CrateId) -> Arc<CrateDefMap>; | 53 | fn crate_def_map(&self, krate: CrateId) -> Arc<DefMap>; |
54 | 54 | ||
55 | #[salsa::invoke(CrateDefMap::crate_def_map_query)] | 55 | #[salsa::invoke(DefMap::crate_def_map_query)] |
56 | fn crate_def_map_query(&self, krate: CrateId) -> Arc<CrateDefMap>; | 56 | fn crate_def_map_query(&self, krate: CrateId) -> Arc<DefMap>; |
57 | 57 | ||
58 | #[salsa::invoke(StructData::struct_data_query)] | 58 | #[salsa::invoke(StructData::struct_data_query)] |
59 | fn struct_data(&self, id: StructId) -> Arc<StructData>; | 59 | fn struct_data(&self, id: StructId) -> Arc<StructData>; |
@@ -112,7 +112,7 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { | |||
112 | fn import_map(&self, krate: CrateId) -> Arc<ImportMap>; | 112 | fn import_map(&self, krate: CrateId) -> Arc<ImportMap>; |
113 | } | 113 | } |
114 | 114 | ||
115 | fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> { | 115 | fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<DefMap> { |
116 | let _p = profile::span("crate_def_map:wait"); | 116 | let _p = profile::span("crate_def_map:wait"); |
117 | db.crate_def_map_query(krate) | 117 | db.crate_def_map_query(krate) |
118 | } | 118 | } |
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs index 4a212d291..422a6eeb4 100644 --- a/crates/hir_def/src/find_path.rs +++ b/crates/hir_def/src/find_path.rs | |||
@@ -4,7 +4,7 @@ use hir_expand::name::{known, AsName, Name}; | |||
4 | use rustc_hash::FxHashSet; | 4 | use rustc_hash::FxHashSet; |
5 | use test_utils::mark; | 5 | use test_utils::mark; |
6 | 6 | ||
7 | use crate::nameres::CrateDefMap; | 7 | use crate::nameres::DefMap; |
8 | use crate::{ | 8 | use crate::{ |
9 | db::DefDatabase, | 9 | db::DefDatabase, |
10 | item_scope::ItemInNs, | 10 | item_scope::ItemInNs, |
@@ -47,7 +47,7 @@ impl ModPath { | |||
47 | } | 47 | } |
48 | } | 48 | } |
49 | 49 | ||
50 | fn check_self_super(def_map: &CrateDefMap, item: ItemInNs, from: ModuleId) -> Option<ModPath> { | 50 | fn check_self_super(def_map: &DefMap, item: ItemInNs, from: ModuleId) -> Option<ModPath> { |
51 | if item == ItemInNs::Types(from.into()) { | 51 | if item == ItemInNs::Types(from.into()) { |
52 | // - if the item is the module we're in, use `self` | 52 | // - if the item is the module we're in, use `self` |
53 | Some(ModPath::from_segments(PathKind::Super(0), Vec::new())) | 53 | Some(ModPath::from_segments(PathKind::Super(0), Vec::new())) |
diff --git a/crates/hir_def/src/import_map.rs b/crates/hir_def/src/import_map.rs index e5368b293..fac0de90c 100644 --- a/crates/hir_def/src/import_map.rs +++ b/crates/hir_def/src/import_map.rs | |||
@@ -263,6 +263,7 @@ pub enum ImportKind { | |||
263 | Trait, | 263 | Trait, |
264 | TypeAlias, | 264 | TypeAlias, |
265 | BuiltinType, | 265 | BuiltinType, |
266 | AssociatedItem, | ||
266 | } | 267 | } |
267 | 268 | ||
268 | /// A way to match import map contents against the search query. | 269 | /// A way to match import map contents against the search query. |
@@ -282,6 +283,7 @@ pub struct Query { | |||
282 | query: String, | 283 | query: String, |
283 | lowercased: String, | 284 | lowercased: String, |
284 | name_only: bool, | 285 | name_only: bool, |
286 | assoc_items_only: bool, | ||
285 | search_mode: SearchMode, | 287 | search_mode: SearchMode, |
286 | case_sensitive: bool, | 288 | case_sensitive: bool, |
287 | limit: usize, | 289 | limit: usize, |
@@ -295,6 +297,7 @@ impl Query { | |||
295 | query, | 297 | query, |
296 | lowercased, | 298 | lowercased, |
297 | name_only: false, | 299 | name_only: false, |
300 | assoc_items_only: false, | ||
298 | search_mode: SearchMode::Contains, | 301 | search_mode: SearchMode::Contains, |
299 | case_sensitive: false, | 302 | case_sensitive: false, |
300 | limit: usize::max_value(), | 303 | limit: usize::max_value(), |
@@ -309,6 +312,11 @@ impl Query { | |||
309 | Self { name_only: true, ..self } | 312 | Self { name_only: true, ..self } |
310 | } | 313 | } |
311 | 314 | ||
315 | /// Matches only the entries that are associated items, ignoring the rest. | ||
316 | pub fn assoc_items_only(self) -> Self { | ||
317 | Self { assoc_items_only: true, ..self } | ||
318 | } | ||
319 | |||
312 | /// Specifies the way to search for the entries using the query. | 320 | /// Specifies the way to search for the entries using the query. |
313 | pub fn search_mode(self, search_mode: SearchMode) -> Self { | 321 | pub fn search_mode(self, search_mode: SearchMode) -> Self { |
314 | Self { search_mode, ..self } | 322 | Self { search_mode, ..self } |
@@ -331,6 +339,14 @@ impl Query { | |||
331 | } | 339 | } |
332 | 340 | ||
333 | fn import_matches(&self, import: &ImportInfo, enforce_lowercase: bool) -> bool { | 341 | fn import_matches(&self, import: &ImportInfo, enforce_lowercase: bool) -> bool { |
342 | if import.is_trait_assoc_item { | ||
343 | if self.exclude_import_kinds.contains(&ImportKind::AssociatedItem) { | ||
344 | return false; | ||
345 | } | ||
346 | } else if self.assoc_items_only { | ||
347 | return false; | ||
348 | } | ||
349 | |||
334 | let mut input = if import.is_trait_assoc_item || self.name_only { | 350 | let mut input = if import.is_trait_assoc_item || self.name_only { |
335 | import.path.segments.last().unwrap().to_string() | 351 | import.path.segments.last().unwrap().to_string() |
336 | } else { | 352 | } else { |
@@ -814,6 +830,56 @@ mod tests { | |||
814 | } | 830 | } |
815 | 831 | ||
816 | #[test] | 832 | #[test] |
833 | fn assoc_items_filtering() { | ||
834 | let ra_fixture = r#" | ||
835 | //- /main.rs crate:main deps:dep | ||
836 | //- /dep.rs crate:dep | ||
837 | pub mod fmt { | ||
838 | pub trait Display { | ||
839 | type FmtTypeAlias; | ||
840 | const FMT_CONST: bool; | ||
841 | |||
842 | fn format_function(); | ||
843 | fn format_method(&self); | ||
844 | } | ||
845 | } | ||
846 | "#; | ||
847 | |||
848 | check_search( | ||
849 | ra_fixture, | ||
850 | "main", | ||
851 | Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy).assoc_items_only(), | ||
852 | expect![[r#" | ||
853 | dep::fmt::Display::FMT_CONST (a) | ||
854 | dep::fmt::Display::format_function (a) | ||
855 | dep::fmt::Display::format_method (a) | ||
856 | "#]], | ||
857 | ); | ||
858 | |||
859 | check_search( | ||
860 | ra_fixture, | ||
861 | "main", | ||
862 | Query::new("fmt".to_string()) | ||
863 | .search_mode(SearchMode::Fuzzy) | ||
864 | .exclude_import_kind(ImportKind::AssociatedItem), | ||
865 | expect![[r#" | ||
866 | dep::fmt (t) | ||
867 | dep::fmt::Display (t) | ||
868 | "#]], | ||
869 | ); | ||
870 | |||
871 | check_search( | ||
872 | ra_fixture, | ||
873 | "main", | ||
874 | Query::new("fmt".to_string()) | ||
875 | .search_mode(SearchMode::Fuzzy) | ||
876 | .assoc_items_only() | ||
877 | .exclude_import_kind(ImportKind::AssociatedItem), | ||
878 | expect![[r#""#]], | ||
879 | ); | ||
880 | } | ||
881 | |||
882 | #[test] | ||
817 | fn search_mode() { | 883 | fn search_mode() { |
818 | let ra_fixture = r#" | 884 | let ra_fixture = r#" |
819 | //- /main.rs crate:main deps:dep | 885 | //- /main.rs crate:main deps:dep |
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs index 50acc3f54..769a557ad 100644 --- a/crates/hir_def/src/nameres.rs +++ b/crates/hir_def/src/nameres.rs | |||
@@ -74,7 +74,7 @@ use crate::{ | |||
74 | 74 | ||
75 | /// Contains all top-level defs from a macro-expanded crate | 75 | /// Contains all top-level defs from a macro-expanded crate |
76 | #[derive(Debug, PartialEq, Eq)] | 76 | #[derive(Debug, PartialEq, Eq)] |
77 | pub struct CrateDefMap { | 77 | pub struct DefMap { |
78 | pub root: LocalModuleId, | 78 | pub root: LocalModuleId, |
79 | pub modules: Arena<ModuleData>, | 79 | pub modules: Arena<ModuleData>, |
80 | pub(crate) krate: CrateId, | 80 | pub(crate) krate: CrateId, |
@@ -88,7 +88,7 @@ pub struct CrateDefMap { | |||
88 | diagnostics: Vec<DefDiagnostic>, | 88 | diagnostics: Vec<DefDiagnostic>, |
89 | } | 89 | } |
90 | 90 | ||
91 | impl std::ops::Index<LocalModuleId> for CrateDefMap { | 91 | impl std::ops::Index<LocalModuleId> for DefMap { |
92 | type Output = ModuleData; | 92 | type Output = ModuleData; |
93 | fn index(&self, id: LocalModuleId) -> &ModuleData { | 93 | fn index(&self, id: LocalModuleId) -> &ModuleData { |
94 | &self.modules[id] | 94 | &self.modules[id] |
@@ -169,8 +169,8 @@ pub struct ModuleData { | |||
169 | pub origin: ModuleOrigin, | 169 | pub origin: ModuleOrigin, |
170 | } | 170 | } |
171 | 171 | ||
172 | impl CrateDefMap { | 172 | impl DefMap { |
173 | pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<CrateDefMap> { | 173 | pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<DefMap> { |
174 | let _p = profile::span("crate_def_map_query").detail(|| { | 174 | let _p = profile::span("crate_def_map_query").detail(|| { |
175 | db.crate_graph()[krate].display_name.as_deref().unwrap_or_default().to_string() | 175 | db.crate_graph()[krate].display_name.as_deref().unwrap_or_default().to_string() |
176 | }); | 176 | }); |
@@ -178,7 +178,7 @@ impl CrateDefMap { | |||
178 | let edition = db.crate_graph()[krate].edition; | 178 | let edition = db.crate_graph()[krate].edition; |
179 | let mut modules: Arena<ModuleData> = Arena::default(); | 179 | let mut modules: Arena<ModuleData> = Arena::default(); |
180 | let root = modules.alloc(ModuleData::default()); | 180 | let root = modules.alloc(ModuleData::default()); |
181 | CrateDefMap { | 181 | DefMap { |
182 | krate, | 182 | krate, |
183 | edition, | 183 | edition, |
184 | extern_prelude: FxHashMap::default(), | 184 | extern_prelude: FxHashMap::default(), |
@@ -227,7 +227,7 @@ impl CrateDefMap { | |||
227 | go(&mut buf, self, "crate", self.root); | 227 | go(&mut buf, self, "crate", self.root); |
228 | return buf; | 228 | return buf; |
229 | 229 | ||
230 | fn go(buf: &mut String, map: &CrateDefMap, path: &str, module: LocalModuleId) { | 230 | fn go(buf: &mut String, map: &DefMap, path: &str, module: LocalModuleId) { |
231 | format_to!(buf, "{}\n", path); | 231 | format_to!(buf, "{}\n", path); |
232 | 232 | ||
233 | let mut entries: Vec<_> = map.modules[module].scope.resolutions().collect(); | 233 | let mut entries: Vec<_> = map.modules[module].scope.resolutions().collect(); |
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 0cd61698c..61da56340 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -31,7 +31,7 @@ use crate::{ | |||
31 | }, | 31 | }, |
32 | nameres::{ | 32 | nameres::{ |
33 | diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint, | 33 | diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint, |
34 | BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode, | 34 | BuiltinShadowMode, DefMap, ModuleData, ModuleOrigin, ResolveMode, |
35 | }, | 35 | }, |
36 | path::{ImportAlias, ModPath, PathKind}, | 36 | path::{ImportAlias, ModPath, PathKind}, |
37 | per_ns::PerNs, | 37 | per_ns::PerNs, |
@@ -45,7 +45,7 @@ const GLOB_RECURSION_LIMIT: usize = 100; | |||
45 | const EXPANSION_DEPTH_LIMIT: usize = 128; | 45 | const EXPANSION_DEPTH_LIMIT: usize = 128; |
46 | const FIXED_POINT_LIMIT: usize = 8192; | 46 | const FIXED_POINT_LIMIT: usize = 8192; |
47 | 47 | ||
48 | pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: CrateDefMap) -> CrateDefMap { | 48 | pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap) -> DefMap { |
49 | let crate_graph = db.crate_graph(); | 49 | let crate_graph = db.crate_graph(); |
50 | 50 | ||
51 | // populate external prelude | 51 | // populate external prelude |
@@ -210,7 +210,7 @@ struct DefData<'a> { | |||
210 | /// Walks the tree of module recursively | 210 | /// Walks the tree of module recursively |
211 | struct DefCollector<'a> { | 211 | struct DefCollector<'a> { |
212 | db: &'a dyn DefDatabase, | 212 | db: &'a dyn DefDatabase, |
213 | def_map: CrateDefMap, | 213 | def_map: DefMap, |
214 | glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility)>>, | 214 | glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility)>>, |
215 | unresolved_imports: Vec<ImportDirective>, | 215 | unresolved_imports: Vec<ImportDirective>, |
216 | resolved_imports: Vec<ImportDirective>, | 216 | resolved_imports: Vec<ImportDirective>, |
@@ -859,7 +859,7 @@ impl DefCollector<'_> { | |||
859 | .collect(item_tree.top_level_items()); | 859 | .collect(item_tree.top_level_items()); |
860 | } | 860 | } |
861 | 861 | ||
862 | fn finish(mut self) -> CrateDefMap { | 862 | fn finish(mut self) -> DefMap { |
863 | // Emit diagnostics for all remaining unexpanded macros. | 863 | // Emit diagnostics for all remaining unexpanded macros. |
864 | 864 | ||
865 | for directive in &self.unexpanded_macros { | 865 | for directive in &self.unexpanded_macros { |
@@ -1474,7 +1474,7 @@ mod tests { | |||
1474 | 1474 | ||
1475 | use super::*; | 1475 | use super::*; |
1476 | 1476 | ||
1477 | fn do_collect_defs(db: &dyn DefDatabase, def_map: CrateDefMap) -> CrateDefMap { | 1477 | fn do_collect_defs(db: &dyn DefDatabase, def_map: DefMap) -> DefMap { |
1478 | let mut collector = DefCollector { | 1478 | let mut collector = DefCollector { |
1479 | db, | 1479 | db, |
1480 | def_map, | 1480 | def_map, |
@@ -1493,7 +1493,7 @@ mod tests { | |||
1493 | collector.def_map | 1493 | collector.def_map |
1494 | } | 1494 | } |
1495 | 1495 | ||
1496 | fn do_resolve(code: &str) -> CrateDefMap { | 1496 | fn do_resolve(code: &str) -> DefMap { |
1497 | let (db, _file_id) = TestDB::with_single_file(&code); | 1497 | let (db, _file_id) = TestDB::with_single_file(&code); |
1498 | let krate = db.test_crate(); | 1498 | let krate = db.test_crate(); |
1499 | 1499 | ||
@@ -1501,7 +1501,7 @@ mod tests { | |||
1501 | let edition = db.crate_graph()[krate].edition; | 1501 | let edition = db.crate_graph()[krate].edition; |
1502 | let mut modules: Arena<ModuleData> = Arena::default(); | 1502 | let mut modules: Arena<ModuleData> = Arena::default(); |
1503 | let root = modules.alloc(ModuleData::default()); | 1503 | let root = modules.alloc(ModuleData::default()); |
1504 | CrateDefMap { | 1504 | DefMap { |
1505 | krate, | 1505 | krate, |
1506 | edition, | 1506 | edition, |
1507 | extern_prelude: FxHashMap::default(), | 1507 | extern_prelude: FxHashMap::default(), |
diff --git a/crates/hir_def/src/nameres/path_resolution.rs b/crates/hir_def/src/nameres/path_resolution.rs index 88e10574e..096a7d0ac 100644 --- a/crates/hir_def/src/nameres/path_resolution.rs +++ b/crates/hir_def/src/nameres/path_resolution.rs | |||
@@ -19,7 +19,7 @@ use test_utils::mark; | |||
19 | use crate::{ | 19 | use crate::{ |
20 | db::DefDatabase, | 20 | db::DefDatabase, |
21 | item_scope::BUILTIN_SCOPE, | 21 | item_scope::BUILTIN_SCOPE, |
22 | nameres::{BuiltinShadowMode, CrateDefMap}, | 22 | nameres::{BuiltinShadowMode, DefMap}, |
23 | path::{ModPath, PathKind}, | 23 | path::{ModPath, PathKind}, |
24 | per_ns::PerNs, | 24 | per_ns::PerNs, |
25 | visibility::{RawVisibility, Visibility}, | 25 | visibility::{RawVisibility, Visibility}, |
@@ -61,7 +61,7 @@ impl ResolvePathResult { | |||
61 | } | 61 | } |
62 | } | 62 | } |
63 | 63 | ||
64 | impl CrateDefMap { | 64 | impl DefMap { |
65 | pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs { | 65 | pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs { |
66 | self.extern_prelude | 66 | self.extern_prelude |
67 | .get(name) | 67 | .get(name) |
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs index c459fa66d..723481c36 100644 --- a/crates/hir_def/src/nameres/tests.rs +++ b/crates/hir_def/src/nameres/tests.rs | |||
@@ -13,7 +13,7 @@ use test_utils::mark; | |||
13 | 13 | ||
14 | use crate::{db::DefDatabase, nameres::*, test_db::TestDB}; | 14 | use crate::{db::DefDatabase, nameres::*, test_db::TestDB}; |
15 | 15 | ||
16 | fn compute_crate_def_map(ra_fixture: &str) -> Arc<CrateDefMap> { | 16 | fn compute_crate_def_map(ra_fixture: &str) -> Arc<DefMap> { |
17 | let db = TestDB::with_files(ra_fixture); | 17 | let db = TestDB::with_files(ra_fixture); |
18 | let krate = db.crate_graph().iter().next().unwrap(); | 18 | let krate = db.crate_graph().iter().next().unwrap(); |
19 | db.crate_def_map(krate) | 19 | db.crate_def_map(krate) |
diff --git a/crates/hir_def/src/resolver.rs b/crates/hir_def/src/resolver.rs index 85ddc2c47..e7e92c72d 100644 --- a/crates/hir_def/src/resolver.rs +++ b/crates/hir_def/src/resolver.rs | |||
@@ -16,7 +16,7 @@ use crate::{ | |||
16 | expr::{ExprId, PatId}, | 16 | expr::{ExprId, PatId}, |
17 | generics::GenericParams, | 17 | generics::GenericParams, |
18 | item_scope::{BuiltinShadowMode, BUILTIN_SCOPE}, | 18 | item_scope::{BuiltinShadowMode, BUILTIN_SCOPE}, |
19 | nameres::CrateDefMap, | 19 | nameres::DefMap, |
20 | path::{ModPath, PathKind}, | 20 | path::{ModPath, PathKind}, |
21 | per_ns::PerNs, | 21 | per_ns::PerNs, |
22 | visibility::{RawVisibility, Visibility}, | 22 | visibility::{RawVisibility, Visibility}, |
@@ -34,7 +34,7 @@ pub struct Resolver { | |||
34 | // FIXME how to store these best | 34 | // FIXME how to store these best |
35 | #[derive(Debug, Clone)] | 35 | #[derive(Debug, Clone)] |
36 | struct ModuleItemMap { | 36 | struct ModuleItemMap { |
37 | crate_def_map: Arc<CrateDefMap>, | 37 | crate_def_map: Arc<DefMap>, |
38 | module_id: LocalModuleId, | 38 | module_id: LocalModuleId, |
39 | } | 39 | } |
40 | 40 | ||
@@ -425,7 +425,7 @@ impl Resolver { | |||
425 | traits | 425 | traits |
426 | } | 426 | } |
427 | 427 | ||
428 | fn module_scope(&self) -> Option<(&CrateDefMap, LocalModuleId)> { | 428 | fn module_scope(&self) -> Option<(&DefMap, LocalModuleId)> { |
429 | self.scopes.iter().rev().find_map(|scope| match scope { | 429 | self.scopes.iter().rev().find_map(|scope| match scope { |
430 | Scope::ModuleScope(m) => Some((&*m.crate_def_map, m.module_id)), | 430 | Scope::ModuleScope(m) => Some((&*m.crate_def_map, m.module_id)), |
431 | 431 | ||
@@ -588,11 +588,7 @@ impl Resolver { | |||
588 | self.push_scope(Scope::ImplDefScope(impl_def)) | 588 | self.push_scope(Scope::ImplDefScope(impl_def)) |
589 | } | 589 | } |
590 | 590 | ||
591 | fn push_module_scope( | 591 | fn push_module_scope(self, crate_def_map: Arc<DefMap>, module_id: LocalModuleId) -> Resolver { |
592 | self, | ||
593 | crate_def_map: Arc<CrateDefMap>, | ||
594 | module_id: LocalModuleId, | ||
595 | ) -> Resolver { | ||
596 | self.push_scope(Scope::ModuleScope(ModuleItemMap { crate_def_map, module_id })) | 592 | self.push_scope(Scope::ModuleScope(ModuleItemMap { crate_def_map, module_id })) |
597 | } | 593 | } |
598 | 594 | ||
diff --git a/crates/hir_def/src/visibility.rs b/crates/hir_def/src/visibility.rs index f3bc9d680..3134fa43d 100644 --- a/crates/hir_def/src/visibility.rs +++ b/crates/hir_def/src/visibility.rs | |||
@@ -5,7 +5,7 @@ use syntax::ast; | |||
5 | 5 | ||
6 | use crate::{ | 6 | use crate::{ |
7 | db::DefDatabase, | 7 | db::DefDatabase, |
8 | nameres::CrateDefMap, | 8 | nameres::DefMap, |
9 | path::{ModPath, PathKind}, | 9 | path::{ModPath, PathKind}, |
10 | ModuleId, | 10 | ModuleId, |
11 | }; | 11 | }; |
@@ -116,7 +116,7 @@ impl Visibility { | |||
116 | 116 | ||
117 | pub(crate) fn is_visible_from_def_map( | 117 | pub(crate) fn is_visible_from_def_map( |
118 | self, | 118 | self, |
119 | def_map: &CrateDefMap, | 119 | def_map: &DefMap, |
120 | from_module: crate::LocalModuleId, | 120 | from_module: crate::LocalModuleId, |
121 | ) -> bool { | 121 | ) -> bool { |
122 | let to_module = match self { | 122 | let to_module = match self { |
@@ -135,7 +135,7 @@ impl Visibility { | |||
135 | /// | 135 | /// |
136 | /// If there is no subset relation between `self` and `other`, returns `None` (ie. they're only | 136 | /// If there is no subset relation between `self` and `other`, returns `None` (ie. they're only |
137 | /// visible in unrelated modules). | 137 | /// visible in unrelated modules). |
138 | pub(crate) fn max(self, other: Visibility, def_map: &CrateDefMap) -> Option<Visibility> { | 138 | pub(crate) fn max(self, other: Visibility, def_map: &DefMap) -> Option<Visibility> { |
139 | match (self, other) { | 139 | match (self, other) { |
140 | (Visibility::Module(_), Visibility::Public) | 140 | (Visibility::Module(_), Visibility::Public) |
141 | | (Visibility::Public, Visibility::Module(_)) | 141 | | (Visibility::Public, Visibility::Module(_)) |
diff --git a/crates/hir_ty/src/tests.rs b/crates/hir_ty/src/tests.rs index 5ff755321..4a3fcea8d 100644 --- a/crates/hir_ty/src/tests.rs +++ b/crates/hir_ty/src/tests.rs | |||
@@ -18,7 +18,7 @@ use hir_def::{ | |||
18 | db::DefDatabase, | 18 | db::DefDatabase, |
19 | item_scope::ItemScope, | 19 | item_scope::ItemScope, |
20 | keys, | 20 | keys, |
21 | nameres::CrateDefMap, | 21 | nameres::DefMap, |
22 | AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, | 22 | AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, |
23 | }; | 23 | }; |
24 | use hir_expand::{db::AstDatabase, InFile}; | 24 | use hir_expand::{db::AstDatabase, InFile}; |
@@ -221,7 +221,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { | |||
221 | 221 | ||
222 | fn visit_module( | 222 | fn visit_module( |
223 | db: &TestDB, | 223 | db: &TestDB, |
224 | crate_def_map: &CrateDefMap, | 224 | crate_def_map: &DefMap, |
225 | module_id: LocalModuleId, | 225 | module_id: LocalModuleId, |
226 | cb: &mut dyn FnMut(DefWithBodyId), | 226 | cb: &mut dyn FnMut(DefWithBodyId), |
227 | ) { | 227 | ) { |
@@ -249,7 +249,7 @@ fn visit_module( | |||
249 | 249 | ||
250 | fn visit_scope( | 250 | fn visit_scope( |
251 | db: &TestDB, | 251 | db: &TestDB, |
252 | crate_def_map: &CrateDefMap, | 252 | crate_def_map: &DefMap, |
253 | scope: &ItemScope, | 253 | scope: &ItemScope, |
254 | cb: &mut dyn FnMut(DefWithBodyId), | 254 | cb: &mut dyn FnMut(DefWithBodyId), |
255 | ) { | 255 | ) { |
diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs index e8999a7f3..b10a0a78b 100644 --- a/crates/ide/src/call_hierarchy.rs +++ b/crates/ide/src/call_hierarchy.rs | |||
@@ -47,7 +47,7 @@ pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Optio | |||
47 | 47 | ||
48 | let mut calls = CallLocations::default(); | 48 | let mut calls = CallLocations::default(); |
49 | 49 | ||
50 | for (&file_id, references) in refs.info.references().iter() { | 50 | for (&file_id, references) in refs.references().iter() { |
51 | let file = sema.parse(file_id); | 51 | let file = sema.parse(file_id); |
52 | let file = file.syntax(); | 52 | let file = file.syntax(); |
53 | for reference in references { | 53 | for reference in references { |
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs index 4b4afd40c..579d5a308 100644 --- a/crates/ide/src/diagnostics/fixes.rs +++ b/crates/ide/src/diagnostics/fixes.rs | |||
@@ -140,7 +140,7 @@ impl DiagnosticWithFix for IncorrectCase { | |||
140 | rename_with_semantics(sema, file_position, &self.suggested_text).ok()?; | 140 | rename_with_semantics(sema, file_position, &self.suggested_text).ok()?; |
141 | 141 | ||
142 | let label = format!("Rename to {}", self.suggested_text); | 142 | let label = format!("Rename to {}", self.suggested_text); |
143 | Some(Fix::new(&label, rename_changes.info, frange.range)) | 143 | Some(Fix::new(&label, rename_changes, frange.range)) |
144 | } | 144 | } |
145 | } | 145 | } |
146 | 146 | ||
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index de10406bc..1f08d7810 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs | |||
@@ -438,10 +438,10 @@ fn get_symbol_fragment(db: &dyn HirDatabase, field_or_assoc: &FieldOrAssocItem) | |||
438 | FieldOrAssocItem::Field(field) => format!("#structfield.{}", field.name(db)), | 438 | FieldOrAssocItem::Field(field) => format!("#structfield.{}", field.name(db)), |
439 | FieldOrAssocItem::AssocItem(assoc) => match assoc { | 439 | FieldOrAssocItem::AssocItem(assoc) => match assoc { |
440 | AssocItem::Function(function) => { | 440 | AssocItem::Function(function) => { |
441 | let is_trait_method = matches!( | 441 | let is_trait_method = function |
442 | function.as_assoc_item(db).map(|assoc| assoc.container(db)), | 442 | .as_assoc_item(db) |
443 | Some(AssocItemContainer::Trait(..)) | 443 | .and_then(|assoc| assoc.containing_trait(db)) |
444 | ); | 444 | .is_some(); |
445 | // This distinction may get more complicated when specialization is available. | 445 | // This distinction may get more complicated when specialization is available. |
446 | // Rustdoc makes this decision based on whether a method 'has defaultness'. | 446 | // Rustdoc makes this decision based on whether a method 'has defaultness'. |
447 | // Currently this is only the case for provided trait methods. | 447 | // Currently this is only the case for provided trait methods. |
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 6c94c26b5..567b8117e 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs | |||
@@ -369,9 +369,7 @@ impl Analysis { | |||
369 | position: FilePosition, | 369 | position: FilePosition, |
370 | search_scope: Option<SearchScope>, | 370 | search_scope: Option<SearchScope>, |
371 | ) -> Cancelable<Option<ReferenceSearchResult>> { | 371 | ) -> Cancelable<Option<ReferenceSearchResult>> { |
372 | self.with_db(|db| { | 372 | self.with_db(|db| references::find_all_refs(&Semantics::new(db), position, search_scope)) |
373 | references::find_all_refs(&Semantics::new(db), position, search_scope).map(|it| it.info) | ||
374 | }) | ||
375 | } | 373 | } |
376 | 374 | ||
377 | /// Finds all methods and free functions for the file. Does not return tests! | 375 | /// Finds all methods and free functions for the file. Does not return tests! |
@@ -478,6 +476,7 @@ impl Analysis { | |||
478 | position: FilePosition, | 476 | position: FilePosition, |
479 | full_import_path: &str, | 477 | full_import_path: &str, |
480 | imported_name: String, | 478 | imported_name: String, |
479 | import_for_trait_assoc_item: bool, | ||
481 | ) -> Cancelable<Vec<TextEdit>> { | 480 | ) -> Cancelable<Vec<TextEdit>> { |
482 | Ok(self | 481 | Ok(self |
483 | .with_db(|db| { | 482 | .with_db(|db| { |
@@ -487,6 +486,7 @@ impl Analysis { | |||
487 | position, | 486 | position, |
488 | full_import_path, | 487 | full_import_path, |
489 | imported_name, | 488 | imported_name, |
489 | import_for_trait_assoc_item, | ||
490 | ) | 490 | ) |
491 | })? | 491 | })? |
492 | .unwrap_or_default()) | 492 | .unwrap_or_default()) |
@@ -520,7 +520,7 @@ impl Analysis { | |||
520 | &self, | 520 | &self, |
521 | position: FilePosition, | 521 | position: FilePosition, |
522 | new_name: &str, | 522 | new_name: &str, |
523 | ) -> Cancelable<Result<RangeInfo<SourceChange>, RenameError>> { | 523 | ) -> Cancelable<Result<SourceChange, RenameError>> { |
524 | self.with_db(|db| references::rename::rename(db, position, new_name)) | 524 | self.with_db(|db| references::rename::rename(db, position, new_name)) |
525 | } | 525 | } |
526 | 526 | ||
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index df9c31aef..3a4f4d80b 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs | |||
@@ -25,7 +25,7 @@ use syntax::{ | |||
25 | AstNode, SyntaxNode, TextRange, TokenAtOffset, T, | 25 | AstNode, SyntaxNode, TextRange, TokenAtOffset, T, |
26 | }; | 26 | }; |
27 | 27 | ||
28 | use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo}; | 28 | use crate::{display::TryToNav, FilePosition, NavigationTarget}; |
29 | 29 | ||
30 | #[derive(Debug, Clone)] | 30 | #[derive(Debug, Clone)] |
31 | pub struct ReferenceSearchResult { | 31 | pub struct ReferenceSearchResult { |
@@ -41,14 +41,6 @@ pub struct Declaration { | |||
41 | } | 41 | } |
42 | 42 | ||
43 | impl ReferenceSearchResult { | 43 | impl ReferenceSearchResult { |
44 | pub fn declaration(&self) -> &Declaration { | ||
45 | &self.declaration | ||
46 | } | ||
47 | |||
48 | pub fn decl_target(&self) -> &NavigationTarget { | ||
49 | &self.declaration.nav | ||
50 | } | ||
51 | |||
52 | pub fn references(&self) -> &UsageSearchResult { | 44 | pub fn references(&self) -> &UsageSearchResult { |
53 | &self.references | 45 | &self.references |
54 | } | 46 | } |
@@ -87,7 +79,7 @@ pub(crate) fn find_all_refs( | |||
87 | sema: &Semantics<RootDatabase>, | 79 | sema: &Semantics<RootDatabase>, |
88 | position: FilePosition, | 80 | position: FilePosition, |
89 | search_scope: Option<SearchScope>, | 81 | search_scope: Option<SearchScope>, |
90 | ) -> Option<RangeInfo<ReferenceSearchResult>> { | 82 | ) -> Option<ReferenceSearchResult> { |
91 | let _p = profile::span("find_all_refs"); | 83 | let _p = profile::span("find_all_refs"); |
92 | let syntax = sema.parse(position.file_id).syntax().clone(); | 84 | let syntax = sema.parse(position.file_id).syntax().clone(); |
93 | 85 | ||
@@ -105,7 +97,7 @@ pub(crate) fn find_all_refs( | |||
105 | ) | 97 | ) |
106 | }; | 98 | }; |
107 | 99 | ||
108 | let RangeInfo { range, info: def } = find_name(&sema, &syntax, position, opt_name)?; | 100 | let def = find_name(&sema, &syntax, position, opt_name)?; |
109 | 101 | ||
110 | let mut usages = def.usages(sema).set_scope(search_scope).all(); | 102 | let mut usages = def.usages(sema).set_scope(search_scope).all(); |
111 | usages | 103 | usages |
@@ -139,7 +131,7 @@ pub(crate) fn find_all_refs( | |||
139 | 131 | ||
140 | let declaration = Declaration { nav, kind, access: decl_access(&def, &syntax, decl_range) }; | 132 | let declaration = Declaration { nav, kind, access: decl_access(&def, &syntax, decl_range) }; |
141 | 133 | ||
142 | Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references: usages })) | 134 | Some(ReferenceSearchResult { declaration, references: usages }) |
143 | } | 135 | } |
144 | 136 | ||
145 | fn find_name( | 137 | fn find_name( |
@@ -147,35 +139,27 @@ fn find_name( | |||
147 | syntax: &SyntaxNode, | 139 | syntax: &SyntaxNode, |
148 | position: FilePosition, | 140 | position: FilePosition, |
149 | opt_name: Option<ast::Name>, | 141 | opt_name: Option<ast::Name>, |
150 | ) -> Option<RangeInfo<Definition>> { | 142 | ) -> Option<Definition> { |
151 | if let Some(name) = opt_name { | 143 | let def = if let Some(name) = opt_name { |
152 | let def = NameClass::classify(sema, &name)?.referenced_or_defined(sema.db); | 144 | NameClass::classify(sema, &name)?.referenced_or_defined(sema.db) |
153 | let FileRange { range, .. } = sema.original_range(name.syntax()); | 145 | } else if let Some(lifetime) = |
154 | return Some(RangeInfo::new(range, def)); | ||
155 | } | ||
156 | |||
157 | let (FileRange { range, .. }, def) = if let Some(lifetime) = | ||
158 | sema.find_node_at_offset_with_descend::<ast::Lifetime>(&syntax, position.offset) | 146 | sema.find_node_at_offset_with_descend::<ast::Lifetime>(&syntax, position.offset) |
159 | { | 147 | { |
160 | if let Some(def) = NameRefClass::classify_lifetime(sema, &lifetime) | 148 | if let Some(def) = |
161 | .map(|class| NameRefClass::referenced(class, sema.db)) | 149 | NameRefClass::classify_lifetime(sema, &lifetime).map(|class| class.referenced(sema.db)) |
162 | { | 150 | { |
163 | (sema.original_range(lifetime.syntax()), def) | 151 | def |
164 | } else { | 152 | } else { |
165 | ( | 153 | NameClass::classify_lifetime(sema, &lifetime)?.referenced_or_defined(sema.db) |
166 | sema.original_range(lifetime.syntax()), | ||
167 | NameClass::classify_lifetime(sema, &lifetime)?.referenced_or_defined(sema.db), | ||
168 | ) | ||
169 | } | 154 | } |
155 | } else if let Some(name_ref) = | ||
156 | sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset) | ||
157 | { | ||
158 | NameRefClass::classify(sema, &name_ref)?.referenced(sema.db) | ||
170 | } else { | 159 | } else { |
171 | let name_ref = | 160 | return None; |
172 | sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset)?; | ||
173 | ( | ||
174 | sema.original_range(name_ref.syntax()), | ||
175 | NameRefClass::classify(sema, &name_ref)?.referenced(sema.db), | ||
176 | ) | ||
177 | }; | 161 | }; |
178 | Some(RangeInfo::new(range, def)) | 162 | Some(def) |
179 | } | 163 | } |
180 | 164 | ||
181 | fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> { | 165 | fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> { |
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index c5010df0a..c25bcce50 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs | |||
@@ -63,7 +63,7 @@ pub(crate) fn rename( | |||
63 | db: &RootDatabase, | 63 | db: &RootDatabase, |
64 | position: FilePosition, | 64 | position: FilePosition, |
65 | new_name: &str, | 65 | new_name: &str, |
66 | ) -> RenameResult<RangeInfo<SourceChange>> { | 66 | ) -> RenameResult<SourceChange> { |
67 | let sema = Semantics::new(db); | 67 | let sema = Semantics::new(db); |
68 | rename_with_semantics(&sema, position, new_name) | 68 | rename_with_semantics(&sema, position, new_name) |
69 | } | 69 | } |
@@ -72,7 +72,7 @@ pub(crate) fn rename_with_semantics( | |||
72 | sema: &Semantics<RootDatabase>, | 72 | sema: &Semantics<RootDatabase>, |
73 | position: FilePosition, | 73 | position: FilePosition, |
74 | new_name: &str, | 74 | new_name: &str, |
75 | ) -> RenameResult<RangeInfo<SourceChange>> { | 75 | ) -> RenameResult<SourceChange> { |
76 | let source_file = sema.parse(position.file_id); | 76 | let source_file = sema.parse(position.file_id); |
77 | let syntax = source_file.syntax(); | 77 | let syntax = source_file.syntax(); |
78 | 78 | ||
@@ -91,7 +91,7 @@ pub(crate) fn will_rename_file( | |||
91 | ) -> Option<SourceChange> { | 91 | ) -> Option<SourceChange> { |
92 | let sema = Semantics::new(db); | 92 | let sema = Semantics::new(db); |
93 | let module = sema.to_module_def(file_id)?; | 93 | let module = sema.to_module_def(file_id)?; |
94 | let mut change = rename_mod(&sema, module, new_name_stem).ok()?.info; | 94 | let mut change = rename_mod(&sema, module, new_name_stem).ok()?; |
95 | change.file_system_edits.clear(); | 95 | change.file_system_edits.clear(); |
96 | Some(change) | 96 | Some(change) |
97 | } | 97 | } |
@@ -243,7 +243,7 @@ fn rename_mod( | |||
243 | sema: &Semantics<RootDatabase>, | 243 | sema: &Semantics<RootDatabase>, |
244 | module: Module, | 244 | module: Module, |
245 | new_name: &str, | 245 | new_name: &str, |
246 | ) -> RenameResult<RangeInfo<SourceChange>> { | 246 | ) -> RenameResult<SourceChange> { |
247 | if IdentifierKind::Ident != check_identifier(new_name)? { | 247 | if IdentifierKind::Ident != check_identifier(new_name)? { |
248 | bail!("Invalid name `{0}`: cannot rename module to {0}", new_name); | 248 | bail!("Invalid name `{0}`: cannot rename module to {0}", new_name); |
249 | } | 249 | } |
@@ -281,13 +281,10 @@ fn rename_mod( | |||
281 | }); | 281 | }); |
282 | source_change.extend(ref_edits); | 282 | source_change.extend(ref_edits); |
283 | 283 | ||
284 | Ok(RangeInfo::new(TextRange::default(), source_change)) | 284 | Ok(source_change) |
285 | } | 285 | } |
286 | 286 | ||
287 | fn rename_to_self( | 287 | fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameResult<SourceChange> { |
288 | sema: &Semantics<RootDatabase>, | ||
289 | local: hir::Local, | ||
290 | ) -> RenameResult<RangeInfo<SourceChange>> { | ||
291 | if assert_never!(local.is_self(sema.db)) { | 288 | if assert_never!(local.is_self(sema.db)) { |
292 | bail!("rename_to_self invoked on self"); | 289 | bail!("rename_to_self invoked on self"); |
293 | } | 290 | } |
@@ -356,7 +353,7 @@ fn rename_to_self( | |||
356 | TextEdit::replace(first_param_range, String::from(self_param)), | 353 | TextEdit::replace(first_param_range, String::from(self_param)), |
357 | ); | 354 | ); |
358 | 355 | ||
359 | Ok(RangeInfo::new(TextRange::default(), source_change)) | 356 | Ok(source_change) |
360 | } | 357 | } |
361 | 358 | ||
362 | fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: &str) -> Option<TextEdit> { | 359 | fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: &str) -> Option<TextEdit> { |
@@ -387,7 +384,7 @@ fn rename_self_to_param( | |||
387 | local: hir::Local, | 384 | local: hir::Local, |
388 | new_name: &str, | 385 | new_name: &str, |
389 | identifier_kind: IdentifierKind, | 386 | identifier_kind: IdentifierKind, |
390 | ) -> RenameResult<RangeInfo<SourceChange>> { | 387 | ) -> RenameResult<SourceChange> { |
391 | let (file_id, self_param) = match local.source(sema.db) { | 388 | let (file_id, self_param) = match local.source(sema.db) { |
392 | InFile { file_id, value: Either::Right(self_param) } => (file_id, self_param), | 389 | InFile { file_id, value: Either::Right(self_param) } => (file_id, self_param), |
393 | _ => { | 390 | _ => { |
@@ -408,14 +405,14 @@ fn rename_self_to_param( | |||
408 | source_change.extend(usages.iter().map(|(&file_id, references)| { | 405 | source_change.extend(usages.iter().map(|(&file_id, references)| { |
409 | source_edit_from_references(sema, file_id, &references, new_name) | 406 | source_edit_from_references(sema, file_id, &references, new_name) |
410 | })); | 407 | })); |
411 | Ok(RangeInfo::new(TextRange::default(), source_change)) | 408 | Ok(source_change) |
412 | } | 409 | } |
413 | 410 | ||
414 | fn rename_reference( | 411 | fn rename_reference( |
415 | sema: &Semantics<RootDatabase>, | 412 | sema: &Semantics<RootDatabase>, |
416 | def: Definition, | 413 | def: Definition, |
417 | new_name: &str, | 414 | new_name: &str, |
418 | ) -> RenameResult<RangeInfo<SourceChange>> { | 415 | ) -> RenameResult<SourceChange> { |
419 | let ident_kind = check_identifier(new_name)?; | 416 | let ident_kind = check_identifier(new_name)?; |
420 | 417 | ||
421 | let def_is_lbl_or_lt = matches!(def, | 418 | let def_is_lbl_or_lt = matches!(def, |
@@ -439,7 +436,7 @@ fn rename_reference( | |||
439 | (IdentifierKind::ToSelf, Definition::Local(local)) if local.is_self(sema.db) => { | 436 | (IdentifierKind::ToSelf, Definition::Local(local)) if local.is_self(sema.db) => { |
440 | // no-op | 437 | // no-op |
441 | mark::hit!(rename_self_to_self); | 438 | mark::hit!(rename_self_to_self); |
442 | return Ok(RangeInfo::new(TextRange::default(), SourceChange::default())); | 439 | return Ok(SourceChange::default()); |
443 | } | 440 | } |
444 | (ident_kind, Definition::Local(local)) if local.is_self(sema.db) => { | 441 | (ident_kind, Definition::Local(local)) if local.is_self(sema.db) => { |
445 | mark::hit!(rename_self_to_param); | 442 | mark::hit!(rename_self_to_param); |
@@ -465,7 +462,7 @@ fn rename_reference( | |||
465 | 462 | ||
466 | let (file_id, edit) = source_edit_from_def(sema, def, new_name)?; | 463 | let (file_id, edit) = source_edit_from_def(sema, def, new_name)?; |
467 | source_change.insert_source_edit(file_id, edit); | 464 | source_change.insert_source_edit(file_id, edit); |
468 | Ok(RangeInfo::new(TextRange::default(), source_change)) | 465 | Ok(source_change) |
469 | } | 466 | } |
470 | 467 | ||
471 | fn source_edit_from_def( | 468 | fn source_edit_from_def( |
@@ -518,7 +515,7 @@ mod tests { | |||
518 | Ok(source_change) => { | 515 | Ok(source_change) => { |
519 | let mut text_edit_builder = TextEdit::builder(); | 516 | let mut text_edit_builder = TextEdit::builder(); |
520 | let mut file_id: Option<FileId> = None; | 517 | let mut file_id: Option<FileId> = None; |
521 | for edit in source_change.info.source_file_edits { | 518 | for edit in source_change.source_file_edits { |
522 | file_id = Some(edit.0); | 519 | file_id = Some(edit.0); |
523 | for indel in edit.1.into_iter() { | 520 | for indel in edit.1.into_iter() { |
524 | text_edit_builder.replace(indel.delete, indel.insert); | 521 | text_edit_builder.replace(indel.delete, indel.insert); |
@@ -917,36 +914,33 @@ mod foo$0; | |||
917 | // empty | 914 | // empty |
918 | "#, | 915 | "#, |
919 | expect![[r#" | 916 | expect![[r#" |
920 | RangeInfo { | 917 | SourceChange { |
921 | range: 0..0, | 918 | source_file_edits: { |
922 | info: SourceChange { | 919 | FileId( |
923 | source_file_edits: { | 920 | 1, |
924 | FileId( | 921 | ): TextEdit { |
925 | 1, | 922 | indels: [ |
926 | ): TextEdit { | 923 | Indel { |
927 | indels: [ | 924 | insert: "foo2", |
928 | Indel { | 925 | delete: 4..7, |
929 | insert: "foo2", | 926 | }, |
930 | delete: 4..7, | 927 | ], |
931 | }, | ||
932 | ], | ||
933 | }, | ||
934 | }, | 928 | }, |
935 | file_system_edits: [ | 929 | }, |
936 | MoveFile { | 930 | file_system_edits: [ |
937 | src: FileId( | 931 | MoveFile { |
932 | src: FileId( | ||
933 | 2, | ||
934 | ), | ||
935 | dst: AnchoredPathBuf { | ||
936 | anchor: FileId( | ||
938 | 2, | 937 | 2, |
939 | ), | 938 | ), |
940 | dst: AnchoredPathBuf { | 939 | path: "foo2.rs", |
941 | anchor: FileId( | ||
942 | 2, | ||
943 | ), | ||
944 | path: "foo2.rs", | ||
945 | }, | ||
946 | }, | 940 | }, |
947 | ], | 941 | }, |
948 | is_snippet: false, | 942 | ], |
949 | }, | 943 | is_snippet: false, |
950 | } | 944 | } |
951 | "#]], | 945 | "#]], |
952 | ); | 946 | ); |
@@ -969,46 +963,43 @@ pub struct FooContent; | |||
969 | use crate::foo$0::FooContent; | 963 | use crate::foo$0::FooContent; |
970 | "#, | 964 | "#, |
971 | expect![[r#" | 965 | expect![[r#" |
972 | RangeInfo { | 966 | SourceChange { |
973 | range: 0..0, | 967 | source_file_edits: { |
974 | info: SourceChange { | 968 | FileId( |
975 | source_file_edits: { | 969 | 0, |
976 | FileId( | 970 | ): TextEdit { |
977 | 0, | 971 | indels: [ |
978 | ): TextEdit { | 972 | Indel { |
979 | indels: [ | 973 | insert: "quux", |
980 | Indel { | 974 | delete: 8..11, |
981 | insert: "quux", | 975 | }, |
982 | delete: 8..11, | 976 | ], |
983 | }, | 977 | }, |
984 | ], | 978 | FileId( |
985 | }, | 979 | 2, |
986 | FileId( | 980 | ): TextEdit { |
987 | 2, | 981 | indels: [ |
988 | ): TextEdit { | 982 | Indel { |
989 | indels: [ | 983 | insert: "quux", |
990 | Indel { | 984 | delete: 11..14, |
991 | insert: "quux", | 985 | }, |
992 | delete: 11..14, | 986 | ], |
993 | }, | ||
994 | ], | ||
995 | }, | ||
996 | }, | 987 | }, |
997 | file_system_edits: [ | 988 | }, |
998 | MoveFile { | 989 | file_system_edits: [ |
999 | src: FileId( | 990 | MoveFile { |
991 | src: FileId( | ||
992 | 1, | ||
993 | ), | ||
994 | dst: AnchoredPathBuf { | ||
995 | anchor: FileId( | ||
1000 | 1, | 996 | 1, |
1001 | ), | 997 | ), |
1002 | dst: AnchoredPathBuf { | 998 | path: "quux.rs", |
1003 | anchor: FileId( | ||
1004 | 1, | ||
1005 | ), | ||
1006 | path: "quux.rs", | ||
1007 | }, | ||
1008 | }, | 999 | }, |
1009 | ], | 1000 | }, |
1010 | is_snippet: false, | 1001 | ], |
1011 | }, | 1002 | is_snippet: false, |
1012 | } | 1003 | } |
1013 | "#]], | 1004 | "#]], |
1014 | ); | 1005 | ); |
@@ -1025,36 +1016,33 @@ mod fo$0o; | |||
1025 | // empty | 1016 | // empty |
1026 | "#, | 1017 | "#, |
1027 | expect![[r#" | 1018 | expect![[r#" |
1028 | RangeInfo { | 1019 | SourceChange { |
1029 | range: 0..0, | 1020 | source_file_edits: { |
1030 | info: SourceChange { | 1021 | FileId( |
1031 | source_file_edits: { | 1022 | 0, |
1032 | FileId( | 1023 | ): TextEdit { |
1033 | 0, | 1024 | indels: [ |
1034 | ): TextEdit { | 1025 | Indel { |
1035 | indels: [ | 1026 | insert: "foo2", |
1036 | Indel { | 1027 | delete: 4..7, |
1037 | insert: "foo2", | 1028 | }, |
1038 | delete: 4..7, | 1029 | ], |
1039 | }, | ||
1040 | ], | ||
1041 | }, | ||
1042 | }, | 1030 | }, |
1043 | file_system_edits: [ | 1031 | }, |
1044 | MoveFile { | 1032 | file_system_edits: [ |
1045 | src: FileId( | 1033 | MoveFile { |
1034 | src: FileId( | ||
1035 | 1, | ||
1036 | ), | ||
1037 | dst: AnchoredPathBuf { | ||
1038 | anchor: FileId( | ||
1046 | 1, | 1039 | 1, |
1047 | ), | 1040 | ), |
1048 | dst: AnchoredPathBuf { | 1041 | path: "../foo2/mod.rs", |
1049 | anchor: FileId( | ||
1050 | 1, | ||
1051 | ), | ||
1052 | path: "../foo2/mod.rs", | ||
1053 | }, | ||
1054 | }, | 1042 | }, |
1055 | ], | 1043 | }, |
1056 | is_snippet: false, | 1044 | ], |
1057 | }, | 1045 | is_snippet: false, |
1058 | } | 1046 | } |
1059 | "#]], | 1047 | "#]], |
1060 | ); | 1048 | ); |
@@ -1072,36 +1060,33 @@ mod outer { mod fo$0o; } | |||
1072 | // empty | 1060 | // empty |
1073 | "#, | 1061 | "#, |
1074 | expect![[r#" | 1062 | expect![[r#" |
1075 | RangeInfo { | 1063 | SourceChange { |
1076 | range: 0..0, | 1064 | source_file_edits: { |
1077 | info: SourceChange { | 1065 | FileId( |
1078 | source_file_edits: { | 1066 | 0, |
1079 | FileId( | 1067 | ): TextEdit { |
1080 | 0, | 1068 | indels: [ |
1081 | ): TextEdit { | 1069 | Indel { |
1082 | indels: [ | 1070 | insert: "bar", |
1083 | Indel { | 1071 | delete: 16..19, |
1084 | insert: "bar", | 1072 | }, |
1085 | delete: 16..19, | 1073 | ], |
1086 | }, | ||
1087 | ], | ||
1088 | }, | ||
1089 | }, | 1074 | }, |
1090 | file_system_edits: [ | 1075 | }, |
1091 | MoveFile { | 1076 | file_system_edits: [ |
1092 | src: FileId( | 1077 | MoveFile { |
1078 | src: FileId( | ||
1079 | 1, | ||
1080 | ), | ||
1081 | dst: AnchoredPathBuf { | ||
1082 | anchor: FileId( | ||
1093 | 1, | 1083 | 1, |
1094 | ), | 1084 | ), |
1095 | dst: AnchoredPathBuf { | 1085 | path: "bar.rs", |
1096 | anchor: FileId( | ||
1097 | 1, | ||
1098 | ), | ||
1099 | path: "bar.rs", | ||
1100 | }, | ||
1101 | }, | 1086 | }, |
1102 | ], | 1087 | }, |
1103 | is_snippet: false, | 1088 | ], |
1104 | }, | 1089 | is_snippet: false, |
1105 | } | 1090 | } |
1106 | "#]], | 1091 | "#]], |
1107 | ); | 1092 | ); |
@@ -1142,46 +1127,43 @@ pub mod foo$0; | |||
1142 | // pub fn fun() {} | 1127 | // pub fn fun() {} |
1143 | "#, | 1128 | "#, |
1144 | expect![[r#" | 1129 | expect![[r#" |
1145 | RangeInfo { | 1130 | SourceChange { |
1146 | range: 0..0, | 1131 | source_file_edits: { |
1147 | info: SourceChange { | 1132 | FileId( |
1148 | source_file_edits: { | 1133 | 0, |
1149 | FileId( | 1134 | ): TextEdit { |
1150 | 0, | 1135 | indels: [ |
1151 | ): TextEdit { | 1136 | Indel { |
1152 | indels: [ | 1137 | insert: "foo2", |
1153 | Indel { | 1138 | delete: 27..30, |
1154 | insert: "foo2", | 1139 | }, |
1155 | delete: 27..30, | 1140 | ], |
1156 | }, | 1141 | }, |
1157 | ], | 1142 | FileId( |
1158 | }, | 1143 | 1, |
1159 | FileId( | 1144 | ): TextEdit { |
1160 | 1, | 1145 | indels: [ |
1161 | ): TextEdit { | 1146 | Indel { |
1162 | indels: [ | 1147 | insert: "foo2", |
1163 | Indel { | 1148 | delete: 8..11, |
1164 | insert: "foo2", | 1149 | }, |
1165 | delete: 8..11, | 1150 | ], |
1166 | }, | ||
1167 | ], | ||
1168 | }, | ||
1169 | }, | 1151 | }, |
1170 | file_system_edits: [ | 1152 | }, |
1171 | MoveFile { | 1153 | file_system_edits: [ |
1172 | src: FileId( | 1154 | MoveFile { |
1155 | src: FileId( | ||
1156 | 2, | ||
1157 | ), | ||
1158 | dst: AnchoredPathBuf { | ||
1159 | anchor: FileId( | ||
1173 | 2, | 1160 | 2, |
1174 | ), | 1161 | ), |
1175 | dst: AnchoredPathBuf { | 1162 | path: "foo2.rs", |
1176 | anchor: FileId( | ||
1177 | 2, | ||
1178 | ), | ||
1179 | path: "foo2.rs", | ||
1180 | }, | ||
1181 | }, | 1163 | }, |
1182 | ], | 1164 | }, |
1183 | is_snippet: false, | 1165 | ], |
1184 | }, | 1166 | is_snippet: false, |
1185 | } | 1167 | } |
1186 | "#]], | 1168 | "#]], |
1187 | ); | 1169 | ); |
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs index edc3da318..517abbb4b 100644 --- a/crates/ide_db/src/helpers/import_assets.rs +++ b/crates/ide_db/src/helpers/import_assets.rs | |||
@@ -1,12 +1,13 @@ | |||
1 | //! Look up accessible paths for items. | 1 | //! Look up accessible paths for items. |
2 | use either::Either; | 2 | use either::Either; |
3 | use hir::{AsAssocItem, AssocItemContainer, ModuleDef, Semantics}; | 3 | use hir::{AsAssocItem, AssocItem, Crate, MacroDef, Module, ModuleDef, PrefixKind, Semantics}; |
4 | use rustc_hash::FxHashSet; | 4 | use rustc_hash::FxHashSet; |
5 | use syntax::{ast, AstNode, SyntaxNode}; | 5 | use syntax::{ast, AstNode}; |
6 | 6 | ||
7 | use crate::{imports_locator, RootDatabase}; | 7 | use crate::{ |
8 | 8 | imports_locator::{self, AssocItemSearch, DEFAULT_QUERY_SEARCH_LIMIT}, | |
9 | use super::insert_use::InsertUseConfig; | 9 | RootDatabase, |
10 | }; | ||
10 | 11 | ||
11 | #[derive(Debug)] | 12 | #[derive(Debug)] |
12 | pub enum ImportCandidate { | 13 | pub enum ImportCandidate { |
@@ -24,86 +25,141 @@ pub enum ImportCandidate { | |||
24 | 25 | ||
25 | #[derive(Debug)] | 26 | #[derive(Debug)] |
26 | pub struct TraitImportCandidate { | 27 | pub struct TraitImportCandidate { |
27 | pub ty: hir::Type, | 28 | pub receiver_ty: hir::Type, |
28 | pub name: ast::NameRef, | 29 | pub name: NameToImport, |
29 | } | 30 | } |
30 | 31 | ||
31 | #[derive(Debug)] | 32 | #[derive(Debug)] |
32 | pub struct PathImportCandidate { | 33 | pub struct PathImportCandidate { |
33 | pub qualifier: Option<ast::Path>, | 34 | pub qualifier: Option<ast::Path>, |
34 | pub name: ast::NameRef, | 35 | pub name: NameToImport, |
36 | } | ||
37 | |||
38 | #[derive(Debug)] | ||
39 | pub enum NameToImport { | ||
40 | Exact(String), | ||
41 | Fuzzy(String), | ||
42 | } | ||
43 | |||
44 | impl NameToImport { | ||
45 | pub fn text(&self) -> &str { | ||
46 | match self { | ||
47 | NameToImport::Exact(text) => text.as_str(), | ||
48 | NameToImport::Fuzzy(text) => text.as_str(), | ||
49 | } | ||
50 | } | ||
35 | } | 51 | } |
36 | 52 | ||
37 | #[derive(Debug)] | 53 | #[derive(Debug)] |
38 | pub struct ImportAssets { | 54 | pub struct ImportAssets { |
39 | import_candidate: ImportCandidate, | 55 | import_candidate: ImportCandidate, |
40 | module_with_name_to_import: hir::Module, | 56 | module_with_candidate: hir::Module, |
41 | syntax_under_caret: SyntaxNode, | ||
42 | } | 57 | } |
43 | 58 | ||
44 | impl ImportAssets { | 59 | impl ImportAssets { |
45 | pub fn for_method_call( | 60 | pub fn for_method_call( |
46 | method_call: ast::MethodCallExpr, | 61 | method_call: &ast::MethodCallExpr, |
47 | sema: &Semantics<RootDatabase>, | 62 | sema: &Semantics<RootDatabase>, |
48 | ) -> Option<Self> { | 63 | ) -> Option<Self> { |
49 | let syntax_under_caret = method_call.syntax().to_owned(); | ||
50 | let module_with_name_to_import = sema.scope(&syntax_under_caret).module()?; | ||
51 | Some(Self { | 64 | Some(Self { |
52 | import_candidate: ImportCandidate::for_method_call(sema, &method_call)?, | 65 | import_candidate: ImportCandidate::for_method_call(sema, method_call)?, |
53 | module_with_name_to_import, | 66 | module_with_candidate: sema.scope(method_call.syntax()).module()?, |
54 | syntax_under_caret, | ||
55 | }) | 67 | }) |
56 | } | 68 | } |
57 | 69 | ||
58 | pub fn for_regular_path( | 70 | pub fn for_exact_path( |
59 | path_under_caret: ast::Path, | 71 | fully_qualified_path: &ast::Path, |
60 | sema: &Semantics<RootDatabase>, | 72 | sema: &Semantics<RootDatabase>, |
61 | ) -> Option<Self> { | 73 | ) -> Option<Self> { |
62 | let syntax_under_caret = path_under_caret.syntax().to_owned(); | 74 | let syntax_under_caret = fully_qualified_path.syntax(); |
63 | if syntax_under_caret.ancestors().find_map(ast::Use::cast).is_some() { | 75 | if syntax_under_caret.ancestors().find_map(ast::Use::cast).is_some() { |
64 | return None; | 76 | return None; |
65 | } | 77 | } |
66 | |||
67 | let module_with_name_to_import = sema.scope(&syntax_under_caret).module()?; | ||
68 | Some(Self { | 78 | Some(Self { |
69 | import_candidate: ImportCandidate::for_regular_path(sema, &path_under_caret)?, | 79 | import_candidate: ImportCandidate::for_regular_path(sema, fully_qualified_path)?, |
70 | module_with_name_to_import, | 80 | module_with_candidate: sema.scope(syntax_under_caret).module()?, |
71 | syntax_under_caret, | ||
72 | }) | 81 | }) |
73 | } | 82 | } |
74 | 83 | ||
75 | pub fn syntax_under_caret(&self) -> &SyntaxNode { | 84 | pub fn for_fuzzy_path( |
76 | &self.syntax_under_caret | 85 | module_with_path: Module, |
86 | qualifier: Option<ast::Path>, | ||
87 | fuzzy_name: String, | ||
88 | sema: &Semantics<RootDatabase>, | ||
89 | ) -> Option<Self> { | ||
90 | Some(match qualifier { | ||
91 | Some(qualifier) => { | ||
92 | let qualifier_resolution = sema.resolve_path(&qualifier)?; | ||
93 | match qualifier_resolution { | ||
94 | hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => Self { | ||
95 | import_candidate: ImportCandidate::TraitAssocItem(TraitImportCandidate { | ||
96 | receiver_ty: assoc_item_path.ty(sema.db), | ||
97 | name: NameToImport::Fuzzy(fuzzy_name), | ||
98 | }), | ||
99 | module_with_candidate: module_with_path, | ||
100 | }, | ||
101 | _ => Self { | ||
102 | import_candidate: ImportCandidate::Path(PathImportCandidate { | ||
103 | qualifier: Some(qualifier), | ||
104 | name: NameToImport::Fuzzy(fuzzy_name), | ||
105 | }), | ||
106 | module_with_candidate: module_with_path, | ||
107 | }, | ||
108 | } | ||
109 | } | ||
110 | None => Self { | ||
111 | import_candidate: ImportCandidate::Path(PathImportCandidate { | ||
112 | qualifier: None, | ||
113 | name: NameToImport::Fuzzy(fuzzy_name), | ||
114 | }), | ||
115 | module_with_candidate: module_with_path, | ||
116 | }, | ||
117 | }) | ||
77 | } | 118 | } |
78 | 119 | ||
120 | pub fn for_fuzzy_method_call( | ||
121 | module_with_method_call: Module, | ||
122 | receiver_ty: hir::Type, | ||
123 | fuzzy_method_name: String, | ||
124 | ) -> Option<Self> { | ||
125 | Some(Self { | ||
126 | import_candidate: ImportCandidate::TraitMethod(TraitImportCandidate { | ||
127 | receiver_ty, | ||
128 | name: NameToImport::Fuzzy(fuzzy_method_name), | ||
129 | }), | ||
130 | module_with_candidate: module_with_method_call, | ||
131 | }) | ||
132 | } | ||
133 | } | ||
134 | |||
135 | impl ImportAssets { | ||
79 | pub fn import_candidate(&self) -> &ImportCandidate { | 136 | pub fn import_candidate(&self) -> &ImportCandidate { |
80 | &self.import_candidate | 137 | &self.import_candidate |
81 | } | 138 | } |
82 | 139 | ||
83 | fn get_search_query(&self) -> &str { | 140 | fn name_to_import(&self) -> &NameToImport { |
84 | match &self.import_candidate { | 141 | match &self.import_candidate { |
85 | ImportCandidate::Path(candidate) => candidate.name.text(), | 142 | ImportCandidate::Path(candidate) => &candidate.name, |
86 | ImportCandidate::TraitAssocItem(candidate) | 143 | ImportCandidate::TraitAssocItem(candidate) |
87 | | ImportCandidate::TraitMethod(candidate) => candidate.name.text(), | 144 | | ImportCandidate::TraitMethod(candidate) => &candidate.name, |
88 | } | 145 | } |
89 | } | 146 | } |
90 | 147 | ||
91 | pub fn search_for_imports( | 148 | pub fn search_for_imports( |
92 | &self, | 149 | &self, |
93 | sema: &Semantics<RootDatabase>, | 150 | sema: &Semantics<RootDatabase>, |
94 | config: &InsertUseConfig, | 151 | prefix_kind: PrefixKind, |
95 | ) -> Vec<(hir::ModPath, hir::ItemInNs)> { | 152 | ) -> Vec<(hir::ModPath, hir::ItemInNs)> { |
96 | let _p = profile::span("import_assists::search_for_imports"); | 153 | let _p = profile::span("import_assets::search_for_imports"); |
97 | self.search_for(sema, Some(config.prefix_kind)) | 154 | self.search_for(sema, Some(prefix_kind)) |
98 | } | 155 | } |
99 | 156 | ||
100 | /// This may return non-absolute paths if a part of the returned path is already imported into scope. | 157 | /// This may return non-absolute paths if a part of the returned path is already imported into scope. |
101 | #[allow(dead_code)] | ||
102 | pub fn search_for_relative_paths( | 158 | pub fn search_for_relative_paths( |
103 | &self, | 159 | &self, |
104 | sema: &Semantics<RootDatabase>, | 160 | sema: &Semantics<RootDatabase>, |
105 | ) -> Vec<(hir::ModPath, hir::ItemInNs)> { | 161 | ) -> Vec<(hir::ModPath, hir::ItemInNs)> { |
106 | let _p = profile::span("import_assists::search_for_relative_paths"); | 162 | let _p = profile::span("import_assets::search_for_relative_paths"); |
107 | self.search_for(sema, None) | 163 | self.search_for(sema, None) |
108 | } | 164 | } |
109 | 165 | ||
@@ -112,99 +168,142 @@ impl ImportAssets { | |||
112 | sema: &Semantics<RootDatabase>, | 168 | sema: &Semantics<RootDatabase>, |
113 | prefixed: Option<hir::PrefixKind>, | 169 | prefixed: Option<hir::PrefixKind>, |
114 | ) -> Vec<(hir::ModPath, hir::ItemInNs)> { | 170 | ) -> Vec<(hir::ModPath, hir::ItemInNs)> { |
115 | let db = sema.db; | 171 | let current_crate = self.module_with_candidate.krate(); |
116 | let mut trait_candidates = FxHashSet::default(); | ||
117 | let current_crate = self.module_with_name_to_import.krate(); | ||
118 | 172 | ||
119 | let filter = |candidate: Either<hir::ModuleDef, hir::MacroDef>| { | 173 | let unfiltered_imports = match self.name_to_import() { |
120 | trait_candidates.clear(); | 174 | NameToImport::Exact(exact_name) => { |
121 | match &self.import_candidate { | 175 | imports_locator::find_exact_imports(sema, current_crate, exact_name.clone()) |
122 | ImportCandidate::TraitAssocItem(trait_candidate) => { | 176 | } |
123 | let located_assoc_item = match candidate { | 177 | // FIXME: ideally, we should avoid using `fst` for seacrhing trait imports for assoc items: |
124 | Either::Left(ModuleDef::Function(located_function)) => { | 178 | // instead, we need to look up all trait impls for a certain struct and search through them only |
125 | located_function.as_assoc_item(db) | 179 | // see https://github.com/rust-analyzer/rust-analyzer/pull/7293#issuecomment-761585032 |
126 | } | 180 | // and https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Blanket.20trait.20impls.20lookup |
127 | Either::Left(ModuleDef::Const(located_const)) => { | 181 | // for the details |
128 | located_const.as_assoc_item(db) | 182 | NameToImport::Fuzzy(fuzzy_name) => { |
129 | } | 183 | let (assoc_item_search, limit) = match self.import_candidate { |
130 | _ => None, | 184 | ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_) => { |
185 | (AssocItemSearch::AssocItemsOnly, None) | ||
131 | } | 186 | } |
132 | .map(|assoc| assoc.container(db)) | 187 | _ => (AssocItemSearch::Exclude, Some(DEFAULT_QUERY_SEARCH_LIMIT)), |
133 | .and_then(Self::assoc_to_trait)?; | 188 | }; |
134 | 189 | imports_locator::find_similar_imports( | |
135 | trait_candidates.insert(located_assoc_item.into()); | 190 | sema, |
191 | current_crate, | ||
192 | fuzzy_name.clone(), | ||
193 | assoc_item_search, | ||
194 | limit, | ||
195 | ) | ||
196 | } | ||
197 | }; | ||
136 | 198 | ||
137 | trait_candidate | 199 | let db = sema.db; |
138 | .ty | 200 | let mut res = |
139 | .iterate_path_candidates( | 201 | applicable_defs(self.import_candidate(), current_crate, db, unfiltered_imports) |
140 | db, | 202 | .filter_map(|candidate| { |
141 | current_crate, | 203 | let item: hir::ItemInNs = candidate.clone().either(Into::into, Into::into); |
142 | &trait_candidates, | ||
143 | None, | ||
144 | |_, assoc| Self::assoc_to_trait(assoc.container(db)), | ||
145 | ) | ||
146 | .map(ModuleDef::from) | ||
147 | .map(Either::Left) | ||
148 | } | ||
149 | ImportCandidate::TraitMethod(trait_candidate) => { | ||
150 | let located_assoc_item = | ||
151 | if let Either::Left(ModuleDef::Function(located_function)) = candidate { | ||
152 | located_function | ||
153 | .as_assoc_item(db) | ||
154 | .map(|assoc| assoc.container(db)) | ||
155 | .and_then(Self::assoc_to_trait) | ||
156 | } else { | ||
157 | None | ||
158 | }?; | ||
159 | 204 | ||
160 | trait_candidates.insert(located_assoc_item.into()); | 205 | let item_to_search = match self.import_candidate { |
206 | ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_) => { | ||
207 | let canidate_trait = match candidate { | ||
208 | Either::Left(module_def) => { | ||
209 | module_def.as_assoc_item(db)?.containing_trait(db) | ||
210 | } | ||
211 | _ => None, | ||
212 | }?; | ||
213 | ModuleDef::from(canidate_trait).into() | ||
214 | } | ||
215 | _ => item, | ||
216 | }; | ||
161 | 217 | ||
162 | trait_candidate | 218 | if let Some(prefix_kind) = prefixed { |
163 | .ty | 219 | self.module_with_candidate.find_use_path_prefixed( |
164 | .iterate_method_candidates( | ||
165 | db, | 220 | db, |
166 | current_crate, | 221 | item_to_search, |
167 | &trait_candidates, | 222 | prefix_kind, |
168 | None, | ||
169 | |_, function| { | ||
170 | Self::assoc_to_trait(function.as_assoc_item(db)?.container(db)) | ||
171 | }, | ||
172 | ) | 223 | ) |
173 | .map(ModuleDef::from) | 224 | } else { |
174 | .map(Either::Left) | 225 | self.module_with_candidate.find_use_path(db, item_to_search) |
175 | } | 226 | } |
176 | _ => Some(candidate), | 227 | .map(|path| (path, item)) |
177 | } | 228 | }) |
178 | }; | 229 | .filter(|(use_path, _)| use_path.len() > 1) |
179 | 230 | .collect::<Vec<_>>(); | |
180 | let mut res = imports_locator::find_exact_imports( | 231 | res.sort_by_cached_key(|(path, _)| path.clone()); |
181 | sema, | ||
182 | current_crate, | ||
183 | self.get_search_query().to_string(), | ||
184 | ) | ||
185 | .filter_map(filter) | ||
186 | .filter_map(|candidate| { | ||
187 | let item: hir::ItemInNs = candidate.either(Into::into, Into::into); | ||
188 | if let Some(prefix_kind) = prefixed { | ||
189 | self.module_with_name_to_import.find_use_path_prefixed(db, item, prefix_kind) | ||
190 | } else { | ||
191 | self.module_with_name_to_import.find_use_path(db, item) | ||
192 | } | ||
193 | .map(|path| (path, item)) | ||
194 | }) | ||
195 | .filter(|(use_path, _)| use_path.len() > 1) | ||
196 | .take(20) | ||
197 | .collect::<Vec<_>>(); | ||
198 | res.sort_by_key(|(path, _)| path.clone()); | ||
199 | res | 232 | res |
200 | } | 233 | } |
234 | } | ||
201 | 235 | ||
202 | fn assoc_to_trait(assoc: AssocItemContainer) -> Option<hir::Trait> { | 236 | fn applicable_defs<'a>( |
203 | if let AssocItemContainer::Trait(extracted_trait) = assoc { | 237 | import_candidate: &ImportCandidate, |
204 | Some(extracted_trait) | 238 | current_crate: Crate, |
205 | } else { | 239 | db: &RootDatabase, |
206 | None | 240 | unfiltered_imports: Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a>, |
241 | ) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> { | ||
242 | let receiver_ty = match import_candidate { | ||
243 | ImportCandidate::Path(_) => return unfiltered_imports, | ||
244 | ImportCandidate::TraitAssocItem(candidate) | ImportCandidate::TraitMethod(candidate) => { | ||
245 | &candidate.receiver_ty | ||
207 | } | 246 | } |
247 | }; | ||
248 | |||
249 | let mut required_assoc_items = FxHashSet::default(); | ||
250 | |||
251 | let trait_candidates = unfiltered_imports | ||
252 | .filter_map(|input| match input { | ||
253 | Either::Left(module_def) => module_def.as_assoc_item(db), | ||
254 | _ => None, | ||
255 | }) | ||
256 | .filter_map(|assoc| { | ||
257 | let assoc_item_trait = assoc.containing_trait(db)?; | ||
258 | required_assoc_items.insert(assoc); | ||
259 | Some(assoc_item_trait.into()) | ||
260 | }) | ||
261 | .collect(); | ||
262 | |||
263 | let mut applicable_defs = FxHashSet::default(); | ||
264 | |||
265 | match import_candidate { | ||
266 | ImportCandidate::Path(_) => unreachable!(), | ||
267 | ImportCandidate::TraitAssocItem(_) => receiver_ty.iterate_path_candidates( | ||
268 | db, | ||
269 | current_crate, | ||
270 | &trait_candidates, | ||
271 | None, | ||
272 | |_, assoc| { | ||
273 | if required_assoc_items.contains(&assoc) { | ||
274 | if let AssocItem::Function(f) = assoc { | ||
275 | if f.self_param(db).is_some() { | ||
276 | return None; | ||
277 | } | ||
278 | } | ||
279 | applicable_defs.insert(Either::Left(assoc_to_module_def(assoc))); | ||
280 | } | ||
281 | None::<()> | ||
282 | }, | ||
283 | ), | ||
284 | ImportCandidate::TraitMethod(_) => receiver_ty.iterate_method_candidates( | ||
285 | db, | ||
286 | current_crate, | ||
287 | &trait_candidates, | ||
288 | None, | ||
289 | |_, function| { | ||
290 | let assoc = function.as_assoc_item(db)?; | ||
291 | if required_assoc_items.contains(&assoc) { | ||
292 | applicable_defs.insert(Either::Left(assoc_to_module_def(assoc))); | ||
293 | } | ||
294 | None::<()> | ||
295 | }, | ||
296 | ), | ||
297 | }; | ||
298 | |||
299 | Box::new(applicable_defs.into_iter()) | ||
300 | } | ||
301 | |||
302 | fn assoc_to_module_def(assoc: AssocItem) -> ModuleDef { | ||
303 | match assoc { | ||
304 | AssocItem::Function(f) => f.into(), | ||
305 | AssocItem::Const(c) => c.into(), | ||
306 | AssocItem::TypeAlias(t) => t.into(), | ||
208 | } | 307 | } |
209 | } | 308 | } |
210 | 309 | ||
@@ -216,22 +315,19 @@ impl ImportCandidate { | |||
216 | match sema.resolve_method_call(method_call) { | 315 | match sema.resolve_method_call(method_call) { |
217 | Some(_) => None, | 316 | Some(_) => None, |
218 | None => Some(Self::TraitMethod(TraitImportCandidate { | 317 | None => Some(Self::TraitMethod(TraitImportCandidate { |
219 | ty: sema.type_of_expr(&method_call.receiver()?)?, | 318 | receiver_ty: sema.type_of_expr(&method_call.receiver()?)?, |
220 | name: method_call.name_ref()?, | 319 | name: NameToImport::Exact(method_call.name_ref()?.to_string()), |
221 | })), | 320 | })), |
222 | } | 321 | } |
223 | } | 322 | } |
224 | 323 | ||
225 | fn for_regular_path( | 324 | fn for_regular_path(sema: &Semantics<RootDatabase>, path: &ast::Path) -> Option<Self> { |
226 | sema: &Semantics<RootDatabase>, | 325 | if sema.resolve_path(path).is_some() { |
227 | path_under_caret: &ast::Path, | ||
228 | ) -> Option<Self> { | ||
229 | if sema.resolve_path(path_under_caret).is_some() { | ||
230 | return None; | 326 | return None; |
231 | } | 327 | } |
232 | 328 | ||
233 | let segment = path_under_caret.segment()?; | 329 | let segment = path.segment()?; |
234 | let candidate = if let Some(qualifier) = path_under_caret.qualifier() { | 330 | let candidate = if let Some(qualifier) = path.qualifier() { |
235 | let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?; | 331 | let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?; |
236 | let qualifier_start_path = | 332 | let qualifier_start_path = |
237 | qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; | 333 | qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; |
@@ -244,8 +340,8 @@ impl ImportCandidate { | |||
244 | match qualifier_resolution { | 340 | match qualifier_resolution { |
245 | hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => { | 341 | hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => { |
246 | ImportCandidate::TraitAssocItem(TraitImportCandidate { | 342 | ImportCandidate::TraitAssocItem(TraitImportCandidate { |
247 | ty: assoc_item_path.ty(sema.db), | 343 | receiver_ty: assoc_item_path.ty(sema.db), |
248 | name: segment.name_ref()?, | 344 | name: NameToImport::Exact(segment.name_ref()?.to_string()), |
249 | }) | 345 | }) |
250 | } | 346 | } |
251 | _ => return None, | 347 | _ => return None, |
@@ -253,13 +349,15 @@ impl ImportCandidate { | |||
253 | } else { | 349 | } else { |
254 | ImportCandidate::Path(PathImportCandidate { | 350 | ImportCandidate::Path(PathImportCandidate { |
255 | qualifier: Some(qualifier), | 351 | qualifier: Some(qualifier), |
256 | name: qualifier_start, | 352 | name: NameToImport::Exact(qualifier_start.to_string()), |
257 | }) | 353 | }) |
258 | } | 354 | } |
259 | } else { | 355 | } else { |
260 | ImportCandidate::Path(PathImportCandidate { | 356 | ImportCandidate::Path(PathImportCandidate { |
261 | qualifier: None, | 357 | qualifier: None, |
262 | name: segment.syntax().descendants().find_map(ast::NameRef::cast)?, | 358 | name: NameToImport::Exact( |
359 | segment.syntax().descendants().find_map(ast::NameRef::cast)?.to_string(), | ||
360 | ), | ||
263 | }) | 361 | }) |
264 | }; | 362 | }; |
265 | Some(candidate) | 363 | Some(candidate) |
diff --git a/crates/ide_db/src/imports_locator.rs b/crates/ide_db/src/imports_locator.rs index d111fba92..502e8281a 100644 --- a/crates/ide_db/src/imports_locator.rs +++ b/crates/ide_db/src/imports_locator.rs | |||
@@ -1,7 +1,10 @@ | |||
1 | //! This module contains an import search functionality that is provided to the assists module. | 1 | //! This module contains an import search functionality that is provided to the assists module. |
2 | //! Later, this should be moved away to a separate crate that is accessible from the assists module. | 2 | //! Later, this should be moved away to a separate crate that is accessible from the assists module. |
3 | 3 | ||
4 | use hir::{import_map, AsAssocItem, Crate, MacroDef, ModuleDef, Semantics}; | 4 | use hir::{ |
5 | import_map::{self, ImportKind}, | ||
6 | AsAssocItem, Crate, MacroDef, ModuleDef, Semantics, | ||
7 | }; | ||
5 | use syntax::{ast, AstNode, SyntaxKind::NAME}; | 8 | use syntax::{ast, AstNode, SyntaxKind::NAME}; |
6 | 9 | ||
7 | use crate::{ | 10 | use crate::{ |
@@ -12,69 +15,84 @@ use crate::{ | |||
12 | use either::Either; | 15 | use either::Either; |
13 | use rustc_hash::FxHashSet; | 16 | use rustc_hash::FxHashSet; |
14 | 17 | ||
15 | const QUERY_SEARCH_LIMIT: usize = 40; | 18 | pub(crate) const DEFAULT_QUERY_SEARCH_LIMIT: usize = 40; |
16 | 19 | ||
17 | pub fn find_exact_imports<'a>( | 20 | pub fn find_exact_imports<'a>( |
18 | sema: &Semantics<'a, RootDatabase>, | 21 | sema: &Semantics<'a, RootDatabase>, |
19 | krate: Crate, | 22 | krate: Crate, |
20 | name_to_import: String, | 23 | name_to_import: String, |
21 | ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { | 24 | ) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>>> { |
22 | let _p = profile::span("find_exact_imports"); | 25 | let _p = profile::span("find_exact_imports"); |
23 | find_imports( | 26 | Box::new(find_imports( |
24 | sema, | 27 | sema, |
25 | krate, | 28 | krate, |
26 | { | 29 | { |
27 | let mut local_query = symbol_index::Query::new(name_to_import.clone()); | 30 | let mut local_query = symbol_index::Query::new(name_to_import.clone()); |
28 | local_query.exact(); | 31 | local_query.exact(); |
29 | local_query.limit(QUERY_SEARCH_LIMIT); | 32 | local_query.limit(DEFAULT_QUERY_SEARCH_LIMIT); |
30 | local_query | 33 | local_query |
31 | }, | 34 | }, |
32 | import_map::Query::new(name_to_import) | 35 | import_map::Query::new(name_to_import) |
33 | .limit(QUERY_SEARCH_LIMIT) | 36 | .limit(DEFAULT_QUERY_SEARCH_LIMIT) |
34 | .name_only() | 37 | .name_only() |
35 | .search_mode(import_map::SearchMode::Equals) | 38 | .search_mode(import_map::SearchMode::Equals) |
36 | .case_sensitive(), | 39 | .case_sensitive(), |
37 | ) | 40 | )) |
41 | } | ||
42 | |||
43 | pub enum AssocItemSearch { | ||
44 | Include, | ||
45 | Exclude, | ||
46 | AssocItemsOnly, | ||
38 | } | 47 | } |
39 | 48 | ||
40 | pub fn find_similar_imports<'a>( | 49 | pub fn find_similar_imports<'a>( |
41 | sema: &Semantics<'a, RootDatabase>, | 50 | sema: &Semantics<'a, RootDatabase>, |
42 | krate: Crate, | 51 | krate: Crate, |
43 | limit: Option<usize>, | ||
44 | fuzzy_search_string: String, | 52 | fuzzy_search_string: String, |
45 | ignore_assoc_items: bool, | 53 | assoc_item_search: AssocItemSearch, |
46 | name_only: bool, | 54 | limit: Option<usize>, |
47 | ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> + 'a { | 55 | ) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> { |
48 | let _p = profile::span("find_similar_imports"); | 56 | let _p = profile::span("find_similar_imports"); |
49 | 57 | ||
50 | let mut external_query = import_map::Query::new(fuzzy_search_string.clone()) | 58 | let mut external_query = import_map::Query::new(fuzzy_search_string.clone()) |
51 | .search_mode(import_map::SearchMode::Fuzzy); | 59 | .search_mode(import_map::SearchMode::Fuzzy) |
52 | if name_only { | 60 | .name_only(); |
53 | external_query = external_query.name_only(); | 61 | |
62 | match assoc_item_search { | ||
63 | AssocItemSearch::Include => {} | ||
64 | AssocItemSearch::Exclude => { | ||
65 | external_query = external_query.exclude_import_kind(ImportKind::AssociatedItem); | ||
66 | } | ||
67 | AssocItemSearch::AssocItemsOnly => { | ||
68 | external_query = external_query.assoc_items_only(); | ||
69 | } | ||
54 | } | 70 | } |
55 | 71 | ||
56 | let mut local_query = symbol_index::Query::new(fuzzy_search_string); | 72 | let mut local_query = symbol_index::Query::new(fuzzy_search_string); |
57 | 73 | ||
58 | if let Some(limit) = limit { | 74 | if let Some(limit) = limit { |
59 | local_query.limit(limit); | ||
60 | external_query = external_query.limit(limit); | 75 | external_query = external_query.limit(limit); |
76 | local_query.limit(limit); | ||
61 | } | 77 | } |
62 | 78 | ||
63 | let db = sema.db; | 79 | let db = sema.db; |
64 | find_imports(sema, krate, local_query, external_query).filter(move |import_candidate| { | 80 | Box::new(find_imports(sema, krate, local_query, external_query).filter( |
65 | if ignore_assoc_items { | 81 | move |import_candidate| match assoc_item_search { |
66 | match import_candidate { | 82 | AssocItemSearch::Include => true, |
67 | Either::Left(ModuleDef::Function(function)) => function.as_assoc_item(db).is_none(), | 83 | AssocItemSearch::Exclude => !is_assoc_item(import_candidate, db), |
68 | Either::Left(ModuleDef::Const(const_)) => const_.as_assoc_item(db).is_none(), | 84 | AssocItemSearch::AssocItemsOnly => is_assoc_item(import_candidate, db), |
69 | Either::Left(ModuleDef::TypeAlias(type_alias)) => { | 85 | }, |
70 | type_alias.as_assoc_item(db).is_none() | 86 | )) |
71 | } | 87 | } |
72 | _ => true, | 88 | |
73 | } | 89 | fn is_assoc_item(import_candidate: &Either<ModuleDef, MacroDef>, db: &RootDatabase) -> bool { |
74 | } else { | 90 | match import_candidate { |
75 | true | 91 | Either::Left(ModuleDef::Function(function)) => function.as_assoc_item(db).is_some(), |
76 | } | 92 | Either::Left(ModuleDef::Const(const_)) => const_.as_assoc_item(db).is_some(), |
77 | }) | 93 | Either::Left(ModuleDef::TypeAlias(type_alias)) => type_alias.as_assoc_item(db).is_some(), |
94 | _ => false, | ||
95 | } | ||
78 | } | 96 | } |
79 | 97 | ||
80 | fn find_imports<'a>( | 98 | fn find_imports<'a>( |
diff --git a/crates/parser/src/grammar/items/traits.rs b/crates/parser/src/grammar/items/traits.rs index d076974ed..d3327271c 100644 --- a/crates/parser/src/grammar/items/traits.rs +++ b/crates/parser/src/grammar/items/traits.rs | |||
@@ -40,6 +40,10 @@ pub(super) fn impl_(p: &mut Parser) { | |||
40 | type_params::opt_generic_param_list(p); | 40 | type_params::opt_generic_param_list(p); |
41 | } | 41 | } |
42 | 42 | ||
43 | // test impl_def_const | ||
44 | // impl const Send for X {} | ||
45 | p.eat(T![const]); | ||
46 | |||
43 | // FIXME: never type | 47 | // FIXME: never type |
44 | // impl ! {} | 48 | // impl ! {} |
45 | 49 | ||
diff --git a/crates/rust-analyzer/src/cli/analysis_bench.rs b/crates/rust-analyzer/src/cli/analysis_bench.rs index a02c8327f..a01b49822 100644 --- a/crates/rust-analyzer/src/cli/analysis_bench.rs +++ b/crates/rust-analyzer/src/cli/analysis_bench.rs | |||
@@ -93,7 +93,7 @@ impl BenchCmd { | |||
93 | if is_completion { | 93 | if is_completion { |
94 | let options = CompletionConfig { | 94 | let options = CompletionConfig { |
95 | enable_postfix_completions: true, | 95 | enable_postfix_completions: true, |
96 | enable_autoimport_completions: true, | 96 | enable_imports_on_the_fly: true, |
97 | add_call_parenthesis: true, | 97 | add_call_parenthesis: true, |
98 | add_call_argument_snippets: true, | 98 | add_call_argument_snippets: true, |
99 | snippet_cap: SnippetCap::new(true), | 99 | snippet_cap: SnippetCap::new(true), |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index ce9655818..3ddb9e19a 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -559,7 +559,7 @@ impl Config { | |||
559 | pub fn completion(&self) -> CompletionConfig { | 559 | pub fn completion(&self) -> CompletionConfig { |
560 | CompletionConfig { | 560 | CompletionConfig { |
561 | enable_postfix_completions: self.data.completion_postfix_enable, | 561 | enable_postfix_completions: self.data.completion_postfix_enable, |
562 | enable_autoimport_completions: self.data.completion_autoimport_enable | 562 | enable_imports_on_the_fly: self.data.completion_autoimport_enable |
563 | && completion_item_edit_resolve(&self.caps), | 563 | && completion_item_edit_resolve(&self.caps), |
564 | add_call_parenthesis: self.data.completion_addCallParenthesis, | 564 | add_call_parenthesis: self.data.completion_addCallParenthesis, |
565 | add_call_argument_snippets: self.data.completion_addCallArgumentSnippets, | 565 | add_call_argument_snippets: self.data.completion_addCallArgumentSnippets, |
@@ -581,18 +581,7 @@ impl Config { | |||
581 | AssistConfig { | 581 | AssistConfig { |
582 | snippet_cap: SnippetCap::new(self.experimental("snippetTextEdit")), | 582 | snippet_cap: SnippetCap::new(self.experimental("snippetTextEdit")), |
583 | allowed: None, | 583 | allowed: None, |
584 | insert_use: InsertUseConfig { | 584 | insert_use: self.insert_use_config(), |
585 | merge: match self.data.assist_importMergeBehavior { | ||
586 | MergeBehaviorDef::None => None, | ||
587 | MergeBehaviorDef::Full => Some(MergeBehavior::Full), | ||
588 | MergeBehaviorDef::Last => Some(MergeBehavior::Last), | ||
589 | }, | ||
590 | prefix_kind: match self.data.assist_importPrefix { | ||
591 | ImportPrefixDef::Plain => PrefixKind::Plain, | ||
592 | ImportPrefixDef::ByCrate => PrefixKind::ByCrate, | ||
593 | ImportPrefixDef::BySelf => PrefixKind::BySelf, | ||
594 | }, | ||
595 | }, | ||
596 | } | 585 | } |
597 | } | 586 | } |
598 | pub fn call_info_full(&self) -> bool { | 587 | pub fn call_info_full(&self) -> bool { |
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 1a4e0dd32..001f3a37d 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -653,7 +653,7 @@ pub(crate) fn handle_completion( | |||
653 | let mut new_completion_items = | 653 | let mut new_completion_items = |
654 | to_proto::completion_item(&line_index, line_endings, item.clone()); | 654 | to_proto::completion_item(&line_index, line_endings, item.clone()); |
655 | 655 | ||
656 | if completion_config.enable_autoimport_completions { | 656 | if completion_config.enable_imports_on_the_fly { |
657 | for new_item in &mut new_completion_items { | 657 | for new_item in &mut new_completion_items { |
658 | fill_resolve_data(&mut new_item.data, &item, &text_document_position); | 658 | fill_resolve_data(&mut new_item.data, &item, &text_document_position); |
659 | } | 659 | } |
@@ -703,6 +703,7 @@ pub(crate) fn handle_completion_resolve( | |||
703 | FilePosition { file_id, offset }, | 703 | FilePosition { file_id, offset }, |
704 | &resolve_data.full_import_path, | 704 | &resolve_data.full_import_path, |
705 | resolve_data.imported_name, | 705 | resolve_data.imported_name, |
706 | resolve_data.import_for_trait_assoc_item, | ||
706 | )? | 707 | )? |
707 | .into_iter() | 708 | .into_iter() |
708 | .flat_map(|edit| { | 709 | .flat_map(|edit| { |
@@ -809,7 +810,7 @@ pub(crate) fn handle_rename( | |||
809 | 810 | ||
810 | let change = | 811 | let change = |
811 | snap.analysis.rename(position, &*params.new_name)?.map_err(to_proto::rename_error)?; | 812 | snap.analysis.rename(position, &*params.new_name)?.map_err(to_proto::rename_error)?; |
812 | let workspace_edit = to_proto::workspace_edit(&snap, change.info)?; | 813 | let workspace_edit = to_proto::workspace_edit(&snap, change)?; |
813 | Ok(Some(workspace_edit)) | 814 | Ok(Some(workspace_edit)) |
814 | } | 815 | } |
815 | 816 | ||
@@ -1694,6 +1695,7 @@ struct CompletionResolveData { | |||
1694 | position: lsp_types::TextDocumentPositionParams, | 1695 | position: lsp_types::TextDocumentPositionParams, |
1695 | full_import_path: String, | 1696 | full_import_path: String, |
1696 | imported_name: String, | 1697 | imported_name: String, |
1698 | import_for_trait_assoc_item: bool, | ||
1697 | } | 1699 | } |
1698 | 1700 | ||
1699 | fn fill_resolve_data( | 1701 | fn fill_resolve_data( |
@@ -1710,6 +1712,7 @@ fn fill_resolve_data( | |||
1710 | position: position.to_owned(), | 1712 | position: position.to_owned(), |
1711 | full_import_path, | 1713 | full_import_path, |
1712 | imported_name, | 1714 | imported_name, |
1715 | import_for_trait_assoc_item: import_edit.import_for_trait_assoc_item, | ||
1713 | }) | 1716 | }) |
1714 | .unwrap(), | 1717 | .unwrap(), |
1715 | ); | 1718 | ); |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 1ff2d3fea..0e3550002 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -884,7 +884,7 @@ mod tests { | |||
884 | .completions( | 884 | .completions( |
885 | &ide::CompletionConfig { | 885 | &ide::CompletionConfig { |
886 | enable_postfix_completions: true, | 886 | enable_postfix_completions: true, |
887 | enable_autoimport_completions: true, | 887 | enable_imports_on_the_fly: true, |
888 | add_call_parenthesis: true, | 888 | add_call_parenthesis: true, |
889 | add_call_argument_snippets: true, | 889 | add_call_argument_snippets: true, |
890 | snippet_cap: SnippetCap::new(true), | 890 | snippet_cap: SnippetCap::new(true), |
diff --git a/crates/syntax/test_data/parser/inline/ok/0161_impl_def_const.rast b/crates/syntax/test_data/parser/inline/ok/0161_impl_def_const.rast new file mode 100644 index 000000000..dcd39535b --- /dev/null +++ b/crates/syntax/test_data/parser/inline/ok/0161_impl_def_const.rast | |||
@@ -0,0 +1,24 @@ | |||
1 | [email protected] | ||
2 | [email protected] | ||
3 | [email protected] "impl" | ||
4 | [email protected] " " | ||
5 | [email protected] "const" | ||
6 | [email protected] " " | ||
7 | [email protected] | ||
8 | [email protected] | ||
9 | [email protected] | ||
10 | [email protected] | ||
11 | [email protected] "Send" | ||
12 | [email protected] " " | ||
13 | [email protected] "for" | ||
14 | [email protected] " " | ||
15 | [email protected] | ||
16 | [email protected] | ||
17 | [email protected] | ||
18 | [email protected] | ||
19 | [email protected] "X" | ||
20 | [email protected] " " | ||
21 | [email protected] | ||
22 | [email protected] "{" | ||
23 | [email protected] "}" | ||
24 | [email protected] "\n" | ||
diff --git a/crates/syntax/test_data/parser/inline/ok/0161_impl_def_const.rs b/crates/syntax/test_data/parser/inline/ok/0161_impl_def_const.rs new file mode 100644 index 000000000..8d6886469 --- /dev/null +++ b/crates/syntax/test_data/parser/inline/ok/0161_impl_def_const.rs | |||
@@ -0,0 +1 @@ | |||
impl const Send for X {} | |||