aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/hir/src/lib.rs11
-rw-r--r--crates/ide/src/lib.rs2
-rw-r--r--crates/ide_assists/src/handlers/auto_import.rs52
-rw-r--r--crates/ide_assists/src/handlers/qualify_path.rs40
-rw-r--r--crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs33
-rw-r--r--crates/ide_completion/src/completions/flyimport.rs270
-rw-r--r--crates/ide_completion/src/item.rs43
-rw-r--r--crates/ide_completion/src/lib.rs28
-rw-r--r--crates/ide_completion/src/render.rs19
-rw-r--r--crates/ide_db/src/helpers.rs10
-rw-r--r--crates/ide_db/src/helpers/import_assets.rs578
-rw-r--r--crates/ide_db/src/items_locator.rs (renamed from crates/ide_db/src/imports_locator.rs)79
-rw-r--r--crates/ide_db/src/lib.rs2
-rw-r--r--crates/rust-analyzer/src/config.rs38
-rw-r--r--crates/rust-analyzer/src/handlers.rs10
-rw-r--r--crates/syntax/src/ast/make.rs4
-rw-r--r--crates/syntax/src/lib.rs1
-rw-r--r--crates/syntax/src/utils.rs43
-rw-r--r--docs/user/generated_config.adoc322
-rw-r--r--editors/code/package.json48
20 files changed, 1156 insertions, 477 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index e3a332d30..d5a3d9034 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -1114,6 +1114,7 @@ pub enum AssocItem {
1114 Const(Const), 1114 Const(Const),
1115 TypeAlias(TypeAlias), 1115 TypeAlias(TypeAlias),
1116} 1116}
1117#[derive(Debug)]
1117pub enum AssocItemContainer { 1118pub enum AssocItemContainer {
1118 Trait(Trait), 1119 Trait(Trait),
1119 Impl(Impl), 1120 Impl(Impl),
@@ -2136,6 +2137,16 @@ impl ScopeDef {
2136 } 2137 }
2137} 2138}
2138 2139
2140impl From<ItemInNs> for ScopeDef {
2141 fn from(item: ItemInNs) -> Self {
2142 match item {
2143 ItemInNs::Types(id) => ScopeDef::ModuleDef(id.into()),
2144 ItemInNs::Values(id) => ScopeDef::ModuleDef(id.into()),
2145 ItemInNs::Macros(id) => ScopeDef::MacroDef(id.into()),
2146 }
2147 }
2148}
2149
2139pub trait HasVisibility { 2150pub trait HasVisibility {
2140 fn visibility(&self, db: &dyn HirDatabase) -> Visibility; 2151 fn visibility(&self, db: &dyn HirDatabase) -> Visibility;
2141 fn is_visible_from(&self, db: &dyn HirDatabase, module: Module) -> bool { 2152 fn is_visible_from(&self, db: &dyn HirDatabase, module: Module) -> bool {
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index b600178ee..f83ed65d5 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -478,7 +478,6 @@ impl Analysis {
478 position: FilePosition, 478 position: FilePosition,
479 full_import_path: &str, 479 full_import_path: &str,
480 imported_name: String, 480 imported_name: String,
481 import_for_trait_assoc_item: bool,
482 ) -> Cancelable<Vec<TextEdit>> { 481 ) -> Cancelable<Vec<TextEdit>> {
483 Ok(self 482 Ok(self
484 .with_db(|db| { 483 .with_db(|db| {
@@ -488,7 +487,6 @@ impl Analysis {
488 position, 487 position,
489 full_import_path, 488 full_import_path,
490 imported_name, 489 imported_name,
491 import_for_trait_assoc_item,
492 ) 490 )
493 })? 491 })?
494 .unwrap_or_default()) 492 .unwrap_or_default())
diff --git a/crates/ide_assists/src/handlers/auto_import.rs b/crates/ide_assists/src/handlers/auto_import.rs
index 1422224ac..7caee8df0 100644
--- a/crates/ide_assists/src/handlers/auto_import.rs
+++ b/crates/ide_assists/src/handlers/auto_import.rs
@@ -1,7 +1,7 @@
1use ide_db::helpers::{ 1use ide_db::helpers::{
2 import_assets::{ImportAssets, ImportCandidate}, 2 import_assets::{ImportAssets, ImportCandidate},
3 insert_use::{insert_use, ImportScope}, 3 insert_use::{insert_use, ImportScope},
4 mod_path_to_ast, 4 item_name, mod_path_to_ast,
5}; 5};
6use syntax::{ast, AstNode, SyntaxNode}; 6use syntax::{ast, AstNode, SyntaxNode};
7 7
@@ -92,14 +92,19 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
92 let range = ctx.sema.original_range(&syntax_under_caret).range; 92 let range = ctx.sema.original_range(&syntax_under_caret).range;
93 let group = import_group_message(import_assets.import_candidate()); 93 let group = import_group_message(import_assets.import_candidate());
94 let scope = ImportScope::find_insert_use_container(&syntax_under_caret, &ctx.sema)?; 94 let scope = ImportScope::find_insert_use_container(&syntax_under_caret, &ctx.sema)?;
95 for (import, _) in proposed_imports { 95 for import in proposed_imports {
96 let name = match item_name(ctx.db(), import.original_item) {
97 Some(name) => name,
98 None => continue,
99 };
96 acc.add_group( 100 acc.add_group(
97 &group, 101 &group,
98 AssistId("auto_import", AssistKind::QuickFix), 102 AssistId("auto_import", AssistKind::QuickFix),
99 format!("Import `{}`", &import), 103 format!("Import `{}`", name),
100 range, 104 range,
101 |builder| { 105 |builder| {
102 let rewriter = insert_use(&scope, mod_path_to_ast(&import), ctx.config.insert_use); 106 let rewriter =
107 insert_use(&scope, mod_path_to_ast(&import.import_path), ctx.config.insert_use);
103 builder.rewrite(rewriter); 108 builder.rewrite(rewriter);
104 }, 109 },
105 ); 110 );
@@ -125,10 +130,10 @@ fn import_group_message(import_candidate: &ImportCandidate) -> GroupLabel {
125 let name = match import_candidate { 130 let name = match import_candidate {
126 ImportCandidate::Path(candidate) => format!("Import {}", candidate.name.text()), 131 ImportCandidate::Path(candidate) => format!("Import {}", candidate.name.text()),
127 ImportCandidate::TraitAssocItem(candidate) => { 132 ImportCandidate::TraitAssocItem(candidate) => {
128 format!("Import a trait for item {}", candidate.name.text()) 133 format!("Import a trait for item {}", candidate.assoc_item_name.text())
129 } 134 }
130 ImportCandidate::TraitMethod(candidate) => { 135 ImportCandidate::TraitMethod(candidate) => {
131 format!("Import a trait for method {}", candidate.name.text()) 136 format!("Import a trait for method {}", candidate.assoc_item_name.text())
132 } 137 }
133 }; 138 };
134 GroupLabel(name) 139 GroupLabel(name)
@@ -221,41 +226,6 @@ mod tests {
221 } 226 }
222 227
223 #[test] 228 #[test]
224 fn auto_imports_are_merged() {
225 check_assist(
226 auto_import,
227 r"
228 use PubMod::PubStruct1;
229
230 struct Test {
231 test: Pub$0Struct2<u8>,
232 }
233
234 pub mod PubMod {
235 pub struct PubStruct1;
236 pub struct PubStruct2<T> {
237 _t: T,
238 }
239 }
240 ",
241 r"
242 use PubMod::{PubStruct1, PubStruct2};
243
244 struct Test {
245 test: PubStruct2<u8>,
246 }
247
248 pub mod PubMod {
249 pub struct PubStruct1;
250 pub struct PubStruct2<T> {
251 _t: T,
252 }
253 }
254 ",
255 );
256 }
257
258 #[test]
259 fn applicable_when_found_multiple_imports() { 229 fn applicable_when_found_multiple_imports() {
260 check_assist( 230 check_assist(
261 auto_import, 231 auto_import,
diff --git a/crates/ide_assists/src/handlers/qualify_path.rs b/crates/ide_assists/src/handlers/qualify_path.rs
index d3e34e540..272874ae3 100644
--- a/crates/ide_assists/src/handlers/qualify_path.rs
+++ b/crates/ide_assists/src/handlers/qualify_path.rs
@@ -1,7 +1,10 @@
1use std::iter; 1use std::iter;
2 2
3use hir::AsAssocItem; 3use hir::AsAssocItem;
4use ide_db::helpers::{import_assets::ImportCandidate, mod_path_to_ast}; 4use ide_db::helpers::{
5 import_assets::{ImportCandidate, LocatedImport},
6 item_name, mod_path_to_ast,
7};
5use ide_db::RootDatabase; 8use ide_db::RootDatabase;
6use syntax::{ 9use syntax::{
7 ast, 10 ast,
@@ -71,17 +74,17 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
71 }; 74 };
72 75
73 let group_label = group_label(candidate); 76 let group_label = group_label(candidate);
74 for (import, item) in proposed_imports { 77 for import in proposed_imports {
75 acc.add_group( 78 acc.add_group(
76 &group_label, 79 &group_label,
77 AssistId("qualify_path", AssistKind::QuickFix), 80 AssistId("qualify_path", AssistKind::QuickFix),
78 label(candidate, &import), 81 label(ctx.db(), candidate, &import),
79 range, 82 range,
80 |builder| { 83 |builder| {
81 qualify_candidate.qualify( 84 qualify_candidate.qualify(
82 |replace_with: String| builder.replace(range, replace_with), 85 |replace_with: String| builder.replace(range, replace_with),
83 import, 86 &import.import_path,
84 item, 87 import.item_to_import,
85 ) 88 )
86 }, 89 },
87 ); 90 );
@@ -97,8 +100,13 @@ enum QualifyCandidate<'db> {
97} 100}
98 101
99impl QualifyCandidate<'_> { 102impl QualifyCandidate<'_> {
100 fn qualify(&self, mut replacer: impl FnMut(String), import: hir::ModPath, item: hir::ItemInNs) { 103 fn qualify(
101 let import = mod_path_to_ast(&import); 104 &self,
105 mut replacer: impl FnMut(String),
106 import: &hir::ModPath,
107 item: hir::ItemInNs,
108 ) {
109 let import = mod_path_to_ast(import);
102 match self { 110 match self {
103 QualifyCandidate::QualifierStart(segment, generics) => { 111 QualifyCandidate::QualifierStart(segment, generics) => {
104 let generics = generics.as_ref().map_or_else(String::new, ToString::to_string); 112 let generics = generics.as_ref().map_or_else(String::new, ToString::to_string);
@@ -183,23 +191,29 @@ fn item_as_trait(db: &RootDatabase, item: hir::ItemInNs) -> Option<hir::Trait> {
183fn group_label(candidate: &ImportCandidate) -> GroupLabel { 191fn group_label(candidate: &ImportCandidate) -> GroupLabel {
184 let name = match candidate { 192 let name = match candidate {
185 ImportCandidate::Path(it) => &it.name, 193 ImportCandidate::Path(it) => &it.name,
186 ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => &it.name, 194 ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => {
195 &it.assoc_item_name
196 }
187 } 197 }
188 .text(); 198 .text();
189 GroupLabel(format!("Qualify {}", name)) 199 GroupLabel(format!("Qualify {}", name))
190} 200}
191 201
192fn label(candidate: &ImportCandidate, import: &hir::ModPath) -> String { 202fn label(db: &RootDatabase, candidate: &ImportCandidate, import: &LocatedImport) -> String {
203 let display_path = match item_name(db, import.original_item) {
204 Some(display_path) => display_path.to_string(),
205 None => "{unknown}".to_string(),
206 };
193 match candidate { 207 match candidate {
194 ImportCandidate::Path(candidate) => { 208 ImportCandidate::Path(candidate) => {
195 if candidate.qualifier.is_some() { 209 if candidate.qualifier.is_some() {
196 format!("Qualify with `{}`", &import) 210 format!("Qualify with `{}`", display_path)
197 } else { 211 } else {
198 format!("Qualify as `{}`", &import) 212 format!("Qualify as `{}`", display_path)
199 } 213 }
200 } 214 }
201 ImportCandidate::TraitAssocItem(_) => format!("Qualify `{}`", &import), 215 ImportCandidate::TraitAssocItem(_) => format!("Qualify `{}`", display_path),
202 ImportCandidate::TraitMethod(_) => format!("Qualify with cast as `{}`", &import), 216 ImportCandidate::TraitMethod(_) => format!("Qualify with cast as `{}`", display_path),
203 } 217 }
204} 218}
205 219
diff --git a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
index c69bc5cac..88fe2fe90 100644
--- a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -1,5 +1,6 @@
1use hir::ModuleDef;
1use ide_db::helpers::mod_path_to_ast; 2use ide_db::helpers::mod_path_to_ast;
2use ide_db::imports_locator; 3use ide_db::items_locator;
3use itertools::Itertools; 4use itertools::Itertools;
4use syntax::{ 5use syntax::{
5 ast::{self, make, AstNode, NameOwner}, 6 ast::{self, make, AstNode, NameOwner},
@@ -64,22 +65,20 @@ pub(crate) fn replace_derive_with_manual_impl(
64 let current_module = ctx.sema.scope(annotated_name.syntax()).module()?; 65 let current_module = ctx.sema.scope(annotated_name.syntax()).module()?;
65 let current_crate = current_module.krate(); 66 let current_crate = current_module.krate();
66 67
67 let found_traits = imports_locator::find_exact_imports( 68 let found_traits =
68 &ctx.sema, 69 items_locator::with_exact_name(&ctx.sema, current_crate, trait_token.text().to_string())
69 current_crate, 70 .into_iter()
70 trait_token.text().to_string(), 71 .filter_map(|item| match ModuleDef::from(item.as_module_def_id()?) {
71 ) 72 ModuleDef::Trait(trait_) => Some(trait_),
72 .filter_map(|candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate { 73 _ => None,
73 either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_), 74 })
74 _ => None, 75 .flat_map(|trait_| {
75 }) 76 current_module
76 .flat_map(|trait_| { 77 .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_))
77 current_module 78 .as_ref()
78 .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_)) 79 .map(mod_path_to_ast)
79 .as_ref() 80 .zip(Some(trait_))
80 .map(mod_path_to_ast) 81 });
81 .zip(Some(trait_))
82 });
83 82
84 let mut no_traits_found = true; 83 let mut no_traits_found = true;
85 for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) { 84 for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) {
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs
index f34764b61..391a11c91 100644
--- a/crates/ide_completion/src/completions/flyimport.rs
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -21,6 +21,46 @@
21//! ``` 21//! ```
22//! 22//!
23//! Also completes associated items, that require trait imports. 23//! Also completes associated items, that require trait imports.
24//! If any unresolved and/or partially-qualified path predeces the input, it will be taken into account.
25//! Currently, only the imports with their import path ending with the whole qialifier will be proposed
26//! (no fuzzy matching for qualifier).
27//!
28//! ```
29//! mod foo {
30//! pub mod bar {
31//! pub struct Item;
32//!
33//! impl Item {
34//! pub const TEST_ASSOC: usize = 3;
35//! }
36//! }
37//! }
38//!
39//! fn main() {
40//! bar::Item::TEST_A$0
41//! }
42//! ```
43//! ->
44//! ```
45//! use foo::bar;
46//!
47//! mod foo {
48//! pub mod bar {
49//! pub struct Item;
50//!
51//! impl Item {
52//! pub const TEST_ASSOC: usize = 3;
53//! }
54//! }
55//! }
56//!
57//! fn main() {
58//! bar::Item::TEST_ASSOC
59//! }
60//! ```
61//!
62//! NOTE: currently, if an assoc item comes from a trait that's not currently imported and it also has an unresolved and/or partially-qualified path,
63//! no imports will be proposed.
24//! 64//!
25//! .Fuzzy search details 65//! .Fuzzy search details
26//! 66//!
@@ -48,12 +88,12 @@
48//! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding 88//! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding
49//! capability enabled. 89//! capability enabled.
50 90
51use hir::{AsAssocItem, ModPath, ScopeDef}; 91use hir::ModPath;
52use ide_db::helpers::{ 92use ide_db::helpers::{
53 import_assets::{ImportAssets, ImportCandidate}, 93 import_assets::{ImportAssets, ImportCandidate},
54 insert_use::ImportScope, 94 insert_use::ImportScope,
55}; 95};
56use rustc_hash::FxHashSet; 96use itertools::Itertools;
57use syntax::{AstNode, SyntaxNode, T}; 97use syntax::{AstNode, SyntaxNode, T};
58 98
59use crate::{ 99use crate::{
@@ -92,50 +132,26 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
92 &ctx.sema, 132 &ctx.sema,
93 )?; 133 )?;
94 134
95 let scope_definitions = scope_definitions(ctx); 135 acc.add_all(
96 let mut all_mod_paths = import_assets 136 import_assets
97 .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind) 137 .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind)
98 .into_iter() 138 .into_iter()
99 .map(|(mod_path, item_in_ns)| { 139 .sorted_by_key(|located_import| {
100 let scope_item = match item_in_ns { 140 compute_fuzzy_completion_order_key(
101 hir::ItemInNs::Types(id) => ScopeDef::ModuleDef(id.into()), 141 &located_import.import_path,
102 hir::ItemInNs::Values(id) => ScopeDef::ModuleDef(id.into()), 142 &user_input_lowercased,
103 hir::ItemInNs::Macros(id) => ScopeDef::MacroDef(id.into()), 143 )
104 }; 144 })
105 (mod_path, scope_item) 145 .filter_map(|import| {
106 }) 146 render_resolution_with_import(
107 .filter(|(_, proposed_def)| !scope_definitions.contains(proposed_def)) 147 RenderContext::new(ctx),
108 .collect::<Vec<_>>(); 148 ImportEdit { import, scope: import_scope.clone() },
109 all_mod_paths.sort_by_cached_key(|(mod_path, _)| { 149 )
110 compute_fuzzy_completion_order_key(mod_path, &user_input_lowercased) 150 }),
111 }); 151 );
112
113 acc.add_all(all_mod_paths.into_iter().filter_map(|(import_path, definition)| {
114 let import_for_trait_assoc_item = match definition {
115 ScopeDef::ModuleDef(module_def) => module_def
116 .as_assoc_item(ctx.db)
117 .and_then(|assoc| assoc.containing_trait(ctx.db))
118 .is_some(),
119 _ => false,
120 };
121 let import_edit = ImportEdit {
122 import_path,
123 import_scope: import_scope.clone(),
124 import_for_trait_assoc_item,
125 };
126 render_resolution_with_import(RenderContext::new(ctx), import_edit, &definition)
127 }));
128 Some(()) 152 Some(())
129} 153}
130 154
131fn scope_definitions(ctx: &CompletionContext) -> FxHashSet<ScopeDef> {
132 let mut scope_definitions = FxHashSet::default();
133 ctx.scope.process_all_names(&mut |_, scope_def| {
134 scope_definitions.insert(scope_def);
135 });
136 scope_definitions
137}
138
139pub(crate) fn position_for_import<'a>( 155pub(crate) fn position_for_import<'a>(
140 ctx: &'a CompletionContext, 156 ctx: &'a CompletionContext,
141 import_candidate: Option<&ImportCandidate>, 157 import_candidate: Option<&ImportCandidate>,
@@ -160,23 +176,30 @@ fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAs
160 current_module, 176 current_module,
161 ctx.sema.type_of_expr(dot_receiver)?, 177 ctx.sema.type_of_expr(dot_receiver)?,
162 fuzzy_name, 178 fuzzy_name,
179 dot_receiver.syntax().clone(),
163 ) 180 )
164 } else { 181 } else {
165 let fuzzy_name_length = fuzzy_name.len(); 182 let fuzzy_name_length = fuzzy_name.len();
183 let approximate_node = match current_module.definition_source(ctx.db).value {
184 hir::ModuleSource::SourceFile(s) => s.syntax().clone(),
185 hir::ModuleSource::Module(m) => m.syntax().clone(),
186 hir::ModuleSource::BlockExpr(b) => b.syntax().clone(),
187 };
166 let assets_for_path = ImportAssets::for_fuzzy_path( 188 let assets_for_path = ImportAssets::for_fuzzy_path(
167 current_module, 189 current_module,
168 ctx.path_qual.clone(), 190 ctx.path_qual.clone(),
169 fuzzy_name, 191 fuzzy_name,
170 &ctx.sema, 192 &ctx.sema,
171 ); 193 approximate_node,
194 )?;
172 195
173 if matches!(assets_for_path.as_ref()?.import_candidate(), ImportCandidate::Path(_)) 196 if matches!(assets_for_path.import_candidate(), ImportCandidate::Path(_))
174 && fuzzy_name_length < 2 197 && fuzzy_name_length < 2
175 { 198 {
176 cov_mark::hit!(ignore_short_input_for_path); 199 cov_mark::hit!(ignore_short_input_for_path);
177 None 200 None
178 } else { 201 } else {
179 assets_for_path 202 Some(assets_for_path)
180 } 203 }
181 } 204 }
182} 205}
@@ -186,11 +209,11 @@ fn compute_fuzzy_completion_order_key(
186 user_input_lowercased: &str, 209 user_input_lowercased: &str,
187) -> usize { 210) -> usize {
188 cov_mark::hit!(certain_fuzzy_order_test); 211 cov_mark::hit!(certain_fuzzy_order_test);
189 let proposed_import_name = match proposed_mod_path.segments().last() { 212 let import_name = match proposed_mod_path.segments().last() {
190 Some(name) => name.to_string().to_lowercase(), 213 Some(name) => name.to_string().to_lowercase(),
191 None => return usize::MAX, 214 None => return usize::MAX,
192 }; 215 };
193 match proposed_import_name.match_indices(user_input_lowercased).next() { 216 match import_name.match_indices(user_input_lowercased).next() {
194 Some((first_matching_index, _)) => first_matching_index, 217 Some((first_matching_index, _)) => first_matching_index,
195 None => usize::MAX, 218 None => usize::MAX,
196 } 219 }
@@ -773,4 +796,155 @@ fn main() {
773}"#, 796}"#,
774 ); 797 );
775 } 798 }
799
800 #[test]
801 fn unresolved_qualifier() {
802 let fixture = r#"
803mod foo {
804 pub mod bar {
805 pub mod baz {
806 pub struct Item;
807 }
808 }
809}
810
811fn main() {
812 bar::baz::Ite$0
813}"#;
814
815 check(
816 fixture,
817 expect![[r#"
818 st foo::bar::baz::Item
819 "#]],
820 );
821
822 check_edit(
823 "Item",
824 fixture,
825 r#"
826 use foo::bar;
827
828 mod foo {
829 pub mod bar {
830 pub mod baz {
831 pub struct Item;
832 }
833 }
834 }
835
836 fn main() {
837 bar::baz::Item
838 }"#,
839 );
840 }
841
842 #[test]
843 fn unresolved_assoc_item_container() {
844 let fixture = r#"
845mod foo {
846 pub struct Item;
847
848 impl Item {
849 pub const TEST_ASSOC: usize = 3;
850 }
851}
852
853fn main() {
854 Item::TEST_A$0
855}"#;
856
857 check(
858 fixture,
859 expect![[r#"
860 ct TEST_ASSOC (foo::Item)
861 "#]],
862 );
863
864 check_edit(
865 "TEST_ASSOC",
866 fixture,
867 r#"
868use foo::Item;
869
870mod foo {
871 pub struct Item;
872
873 impl Item {
874 pub const TEST_ASSOC: usize = 3;
875 }
876}
877
878fn main() {
879 Item::TEST_ASSOC
880}"#,
881 );
882 }
883
884 #[test]
885 fn unresolved_assoc_item_container_with_path() {
886 let fixture = r#"
887mod foo {
888 pub mod bar {
889 pub struct Item;
890
891 impl Item {
892 pub const TEST_ASSOC: usize = 3;
893 }
894 }
895}
896
897fn main() {
898 bar::Item::TEST_A$0
899}"#;
900
901 check(
902 fixture,
903 expect![[r#"
904 ct TEST_ASSOC (foo::bar::Item)
905 "#]],
906 );
907
908 check_edit(
909 "TEST_ASSOC",
910 fixture,
911 r#"
912use foo::bar;
913
914mod foo {
915 pub mod bar {
916 pub struct Item;
917
918 impl Item {
919 pub const TEST_ASSOC: usize = 3;
920 }
921 }
922}
923
924fn main() {
925 bar::Item::TEST_ASSOC
926}"#,
927 );
928 }
929
930 #[test]
931 fn fuzzy_unresolved_path() {
932 check(
933 r#"
934mod foo {
935 pub mod bar {
936 pub struct Item;
937
938 impl Item {
939 pub const TEST_ASSOC: usize = 3;
940 }
941 }
942}
943
944fn main() {
945 bar::Ass$0
946}"#,
947 expect![[]],
948 )
949 }
776} 950}
diff --git a/crates/ide_completion/src/item.rs b/crates/ide_completion/src/item.rs
index 9b2435c4b..9b039e3e5 100644
--- a/crates/ide_completion/src/item.rs
+++ b/crates/ide_completion/src/item.rs
@@ -2,15 +2,16 @@
2 2
3use std::fmt; 3use std::fmt;
4 4
5use hir::{Documentation, ModPath, Mutability}; 5use hir::{Documentation, Mutability};
6use ide_db::{ 6use ide_db::{
7 helpers::{ 7 helpers::{
8 import_assets::LocatedImport,
8 insert_use::{self, ImportScope, InsertUseConfig}, 9 insert_use::{self, ImportScope, InsertUseConfig},
9 mod_path_to_ast, SnippetCap, 10 mod_path_to_ast, SnippetCap,
10 }, 11 },
11 SymbolKind, 12 SymbolKind,
12}; 13};
13use stdx::{impl_from, never}; 14use stdx::{format_to, impl_from, never};
14use syntax::{algo, TextRange}; 15use syntax::{algo, TextRange};
15use text_edit::TextEdit; 16use text_edit::TextEdit;
16 17
@@ -272,9 +273,8 @@ impl CompletionItem {
272/// An extra import to add after the completion is applied. 273/// An extra import to add after the completion is applied.
273#[derive(Debug, Clone)] 274#[derive(Debug, Clone)]
274pub struct ImportEdit { 275pub struct ImportEdit {
275 pub import_path: ModPath, 276 pub import: LocatedImport,
276 pub import_scope: ImportScope, 277 pub scope: ImportScope,
277 pub import_for_trait_assoc_item: bool,
278} 278}
279 279
280impl ImportEdit { 280impl ImportEdit {
@@ -284,7 +284,7 @@ impl ImportEdit {
284 let _p = profile::span("ImportEdit::to_text_edit"); 284 let _p = profile::span("ImportEdit::to_text_edit");
285 285
286 let rewriter = 286 let rewriter =
287 insert_use::insert_use(&self.import_scope, mod_path_to_ast(&self.import_path), cfg); 287 insert_use::insert_use(&self.scope, mod_path_to_ast(&self.import.import_path), cfg);
288 let old_ast = rewriter.rewrite_root()?; 288 let old_ast = rewriter.rewrite_root()?;
289 let mut import_insert = TextEdit::builder(); 289 let mut import_insert = TextEdit::builder();
290 algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut import_insert); 290 algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut import_insert);
@@ -322,20 +322,19 @@ impl Builder {
322 let mut lookup = self.lookup; 322 let mut lookup = self.lookup;
323 let mut insert_text = self.insert_text; 323 let mut insert_text = self.insert_text;
324 324
325 if let Some(import_to_add) = self.import_to_add.as_ref() { 325 if let Some(original_path) = self
326 if import_to_add.import_for_trait_assoc_item { 326 .import_to_add
327 lookup = lookup.or_else(|| Some(label.clone())); 327 .as_ref()
328 insert_text = insert_text.or_else(|| Some(label.clone())); 328 .and_then(|import_edit| import_edit.import.original_path.as_ref())
329 label = format!("{} ({})", label, import_to_add.import_path); 329 {
330 lookup = lookup.or_else(|| Some(label.clone()));
331 insert_text = insert_text.or_else(|| Some(label.clone()));
332
333 let original_path_label = original_path.to_string();
334 if original_path_label.ends_with(&label) {
335 label = original_path_label;
330 } else { 336 } else {
331 let mut import_path_without_last_segment = import_to_add.import_path.to_owned(); 337 format_to!(label, " ({})", original_path)
332 let _ = import_path_without_last_segment.pop_segment();
333
334 if !import_path_without_last_segment.segments().is_empty() {
335 lookup = lookup.or_else(|| Some(label.clone()));
336 insert_text = insert_text.or_else(|| Some(label.clone()));
337 label = format!("{}::{}", import_path_without_last_segment, label);
338 }
339 } 338 }
340 } 339 }
341 340
@@ -439,9 +438,3 @@ impl Builder {
439 self 438 self
440 } 439 }
441} 440}
442
443impl<'a> Into<CompletionItem> for Builder {
444 fn into(self) -> CompletionItem {
445 self.build()
446 }
447}
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs
index b0b809791..a0c8c374d 100644
--- a/crates/ide_completion/src/lib.rs
+++ b/crates/ide_completion/src/lib.rs
@@ -13,7 +13,9 @@ mod completions;
13 13
14use completions::flyimport::position_for_import; 14use completions::flyimport::position_for_import;
15use ide_db::{ 15use ide_db::{
16 base_db::FilePosition, helpers::insert_use::ImportScope, imports_locator, RootDatabase, 16 base_db::FilePosition,
17 helpers::{import_assets::LocatedImport, insert_use::ImportScope},
18 items_locator, RootDatabase,
17}; 19};
18use text_edit::TextEdit; 20use text_edit::TextEdit;
19 21
@@ -139,25 +141,27 @@ pub fn resolve_completion_edits(
139 position: FilePosition, 141 position: FilePosition,
140 full_import_path: &str, 142 full_import_path: &str,
141 imported_name: String, 143 imported_name: String,
142 import_for_trait_assoc_item: bool,
143) -> Option<Vec<TextEdit>> { 144) -> Option<Vec<TextEdit>> {
144 let ctx = CompletionContext::new(db, position, config)?; 145 let ctx = CompletionContext::new(db, position, config)?;
145 let position_for_import = position_for_import(&ctx, None)?; 146 let position_for_import = position_for_import(&ctx, None)?;
146 let import_scope = ImportScope::find_insert_use_container(position_for_import, &ctx.sema)?; 147 let scope = ImportScope::find_insert_use_container(position_for_import, &ctx.sema)?;
147 148
148 let current_module = ctx.sema.scope(position_for_import).module()?; 149 let current_module = ctx.sema.scope(position_for_import).module()?;
149 let current_crate = current_module.krate(); 150 let current_crate = current_module.krate();
150 151
151 let import_path = imports_locator::find_exact_imports(&ctx.sema, current_crate, imported_name) 152 let (import_path, item_to_import) =
152 .filter_map(|candidate| { 153 items_locator::with_exact_name(&ctx.sema, current_crate, imported_name)
153 let item: hir::ItemInNs = candidate.either(Into::into, Into::into); 154 .into_iter()
154 current_module.find_use_path_prefixed(db, item, config.insert_use.prefix_kind) 155 .filter_map(|candidate| {
155 }) 156 current_module
156 .find(|mod_path| mod_path.to_string() == full_import_path)?; 157 .find_use_path_prefixed(db, candidate, config.insert_use.prefix_kind)
158 .zip(Some(candidate))
159 })
160 .find(|(mod_path, _)| mod_path.to_string() == full_import_path)?;
161 let import =
162 LocatedImport::new(import_path.clone(), item_to_import, item_to_import, Some(import_path));
157 163
158 ImportEdit { import_path, import_scope, import_for_trait_assoc_item } 164 ImportEdit { import, scope }.to_text_edit(config.insert_use).map(|edit| vec![edit])
159 .to_text_edit(config.insert_use)
160 .map(|edit| vec![edit])
161} 165}
162 166
163#[cfg(test)] 167#[cfg(test)]
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index dcfac23c5..fae5685e2 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -13,7 +13,10 @@ mod builder_ext;
13use hir::{ 13use hir::{
14 AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type, 14 AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type,
15}; 15};
16use ide_db::{helpers::SnippetCap, RootDatabase, SymbolKind}; 16use ide_db::{
17 helpers::{item_name, SnippetCap},
18 RootDatabase, SymbolKind,
19};
17use syntax::TextRange; 20use syntax::TextRange;
18 21
19use crate::{ 22use crate::{
@@ -50,18 +53,20 @@ pub(crate) fn render_resolution<'a>(
50pub(crate) fn render_resolution_with_import<'a>( 53pub(crate) fn render_resolution_with_import<'a>(
51 ctx: RenderContext<'a>, 54 ctx: RenderContext<'a>,
52 import_edit: ImportEdit, 55 import_edit: ImportEdit,
53 resolution: &ScopeDef,
54) -> Option<CompletionItem> { 56) -> Option<CompletionItem> {
57 let resolution = ScopeDef::from(import_edit.import.original_item);
55 let local_name = match resolution { 58 let local_name = match resolution {
56 ScopeDef::ModuleDef(ModuleDef::Function(f)) => f.name(ctx.completion.db).to_string(), 59 ScopeDef::ModuleDef(ModuleDef::Function(f)) => f.name(ctx.completion.db).to_string(),
57 ScopeDef::ModuleDef(ModuleDef::Const(c)) => c.name(ctx.completion.db)?.to_string(), 60 ScopeDef::ModuleDef(ModuleDef::Const(c)) => c.name(ctx.completion.db)?.to_string(),
58 ScopeDef::ModuleDef(ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db).to_string(), 61 ScopeDef::ModuleDef(ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db).to_string(),
59 _ => import_edit.import_path.segments().last()?.to_string(), 62 _ => item_name(ctx.db(), import_edit.import.original_item)?.to_string(),
60 }; 63 };
61 Render::new(ctx).render_resolution(local_name, Some(import_edit), resolution).map(|mut item| { 64 Render::new(ctx).render_resolution(local_name, Some(import_edit), &resolution).map(
62 item.completion_kind = CompletionKind::Magic; 65 |mut item| {
63 item 66 item.completion_kind = CompletionKind::Magic;
64 }) 67 item
68 },
69 )
65} 70}
66 71
67/// Interface for data and methods required for items rendering. 72/// Interface for data and methods required for items rendering.
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs
index 3ff77400b..3c95d3cff 100644
--- a/crates/ide_db/src/helpers.rs
+++ b/crates/ide_db/src/helpers.rs
@@ -2,11 +2,19 @@
2pub mod insert_use; 2pub mod insert_use;
3pub mod import_assets; 3pub mod import_assets;
4 4
5use hir::{Crate, Enum, Module, ScopeDef, Semantics, Trait}; 5use hir::{Crate, Enum, ItemInNs, MacroDef, Module, ModuleDef, Name, ScopeDef, Semantics, Trait};
6use syntax::ast::{self, make}; 6use syntax::ast::{self, make};
7 7
8use crate::RootDatabase; 8use crate::RootDatabase;
9 9
10pub fn item_name(db: &RootDatabase, item: ItemInNs) -> Option<Name> {
11 match item {
12 ItemInNs::Types(module_def_id) => ModuleDef::from(module_def_id).name(db),
13 ItemInNs::Values(module_def_id) => ModuleDef::from(module_def_id).name(db),
14 ItemInNs::Macros(macro_def_id) => MacroDef::from(macro_def_id).name(db),
15 }
16}
17
10/// Converts the mod path struct into its ast representation. 18/// Converts the mod path struct into its ast representation.
11pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path { 19pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path {
12 let _p = profile::span("mod_path_to_ast"); 20 let _p = profile::span("mod_path_to_ast");
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs
index 517abbb4b..e03ccd351 100644
--- a/crates/ide_db/src/helpers/import_assets.rs
+++ b/crates/ide_db/src/helpers/import_assets.rs
@@ -1,19 +1,29 @@
1//! Look up accessible paths for items. 1//! Look up accessible paths for items.
2use either::Either; 2use hir::{
3use hir::{AsAssocItem, AssocItem, Crate, MacroDef, Module, ModuleDef, PrefixKind, Semantics}; 3 AsAssocItem, AssocItem, AssocItemContainer, Crate, ItemInNs, MacroDef, ModPath, Module,
4 ModuleDef, PathResolution, PrefixKind, ScopeDef, Semantics, Type,
5};
6use itertools::Itertools;
4use rustc_hash::FxHashSet; 7use rustc_hash::FxHashSet;
5use syntax::{ast, AstNode}; 8use syntax::{ast, utils::path_to_string_stripping_turbo_fish, AstNode, SyntaxNode};
6 9
7use crate::{ 10use crate::{
8 imports_locator::{self, AssocItemSearch, DEFAULT_QUERY_SEARCH_LIMIT}, 11 items_locator::{self, AssocItemSearch, DEFAULT_QUERY_SEARCH_LIMIT},
9 RootDatabase, 12 RootDatabase,
10}; 13};
11 14
15use super::item_name;
16
17/// A candidate for import, derived during various IDE activities:
18/// * completion with imports on the fly proposals
19/// * completion edit resolve requests
20/// * assists
21/// * etc.
12#[derive(Debug)] 22#[derive(Debug)]
13pub enum ImportCandidate { 23pub enum ImportCandidate {
14 // A path, qualified (`std::collections::HashMap`) or not (`HashMap`). 24 /// A path, qualified (`std::collections::HashMap`) or not (`HashMap`).
15 Path(PathImportCandidate), 25 Path(PathImportCandidate),
16 /// A trait associated function (with no self parameter) or associated constant. 26 /// A trait associated function (with no self parameter) or an associated constant.
17 /// For 'test_mod::TestEnum::test_function', `ty` is the `test_mod::TestEnum` expression type 27 /// For 'test_mod::TestEnum::test_function', `ty` is the `test_mod::TestEnum` expression type
18 /// and `name` is the `test_function` 28 /// and `name` is the `test_function`
19 TraitAssocItem(TraitImportCandidate), 29 TraitAssocItem(TraitImportCandidate),
@@ -23,21 +33,40 @@ pub enum ImportCandidate {
23 TraitMethod(TraitImportCandidate), 33 TraitMethod(TraitImportCandidate),
24} 34}
25 35
36/// A trait import needed for a given associated item access.
37/// For `some::path::SomeStruct::ASSOC_`, contains the
38/// type of `some::path::SomeStruct` and `ASSOC_` as the item name.
26#[derive(Debug)] 39#[derive(Debug)]
27pub struct TraitImportCandidate { 40pub struct TraitImportCandidate {
28 pub receiver_ty: hir::Type, 41 /// A type of the item that has the associated item accessed at.
29 pub name: NameToImport, 42 pub receiver_ty: Type,
43 /// The associated item name that the trait to import should contain.
44 pub assoc_item_name: NameToImport,
30} 45}
31 46
47/// Path import for a given name, qualified or not.
32#[derive(Debug)] 48#[derive(Debug)]
33pub struct PathImportCandidate { 49pub struct PathImportCandidate {
34 pub qualifier: Option<ast::Path>, 50 /// Optional qualifier before name.
51 pub qualifier: Option<FirstSegmentUnresolved>,
52 /// The name the item (struct, trait, enum, etc.) should have.
35 pub name: NameToImport, 53 pub name: NameToImport,
36} 54}
37 55
56/// A qualifier that has a first segment and it's unresolved.
57#[derive(Debug)]
58pub struct FirstSegmentUnresolved {
59 fist_segment: ast::NameRef,
60 full_qualifier: ast::Path,
61}
62
63/// A name that will be used during item lookups.
38#[derive(Debug)] 64#[derive(Debug)]
39pub enum NameToImport { 65pub enum NameToImport {
66 /// Requires items with names that exactly match the given string, case-sensitive.
40 Exact(String), 67 Exact(String),
68 /// Requires items with names that case-insensitively contain all letters from the string,
69 /// in the same order, but not necessary adjacent.
41 Fuzzy(String), 70 Fuzzy(String),
42} 71}
43 72
@@ -50,10 +79,12 @@ impl NameToImport {
50 } 79 }
51} 80}
52 81
82/// A struct to find imports in the project, given a certain name (or its part) and the context.
53#[derive(Debug)] 83#[derive(Debug)]
54pub struct ImportAssets { 84pub struct ImportAssets {
55 import_candidate: ImportCandidate, 85 import_candidate: ImportCandidate,
56 module_with_candidate: hir::Module, 86 candidate_node: SyntaxNode,
87 module_with_candidate: Module,
57} 88}
58 89
59impl ImportAssets { 90impl ImportAssets {
@@ -61,9 +92,11 @@ impl ImportAssets {
61 method_call: &ast::MethodCallExpr, 92 method_call: &ast::MethodCallExpr,
62 sema: &Semantics<RootDatabase>, 93 sema: &Semantics<RootDatabase>,
63 ) -> Option<Self> { 94 ) -> Option<Self> {
95 let candidate_node = method_call.syntax().clone();
64 Some(Self { 96 Some(Self {
65 import_candidate: ImportCandidate::for_method_call(sema, method_call)?, 97 import_candidate: ImportCandidate::for_method_call(sema, method_call)?,
66 module_with_candidate: sema.scope(method_call.syntax()).module()?, 98 module_with_candidate: sema.scope(&candidate_node).module()?,
99 candidate_node,
67 }) 100 })
68 } 101 }
69 102
@@ -71,94 +104,94 @@ impl ImportAssets {
71 fully_qualified_path: &ast::Path, 104 fully_qualified_path: &ast::Path,
72 sema: &Semantics<RootDatabase>, 105 sema: &Semantics<RootDatabase>,
73 ) -> Option<Self> { 106 ) -> Option<Self> {
74 let syntax_under_caret = fully_qualified_path.syntax(); 107 let candidate_node = fully_qualified_path.syntax().clone();
75 if syntax_under_caret.ancestors().find_map(ast::Use::cast).is_some() { 108 if candidate_node.ancestors().find_map(ast::Use::cast).is_some() {
76 return None; 109 return None;
77 } 110 }
78 Some(Self { 111 Some(Self {
79 import_candidate: ImportCandidate::for_regular_path(sema, fully_qualified_path)?, 112 import_candidate: ImportCandidate::for_regular_path(sema, fully_qualified_path)?,
80 module_with_candidate: sema.scope(syntax_under_caret).module()?, 113 module_with_candidate: sema.scope(&candidate_node).module()?,
114 candidate_node,
81 }) 115 })
82 } 116 }
83 117
84 pub fn for_fuzzy_path( 118 pub fn for_fuzzy_path(
85 module_with_path: Module, 119 module_with_candidate: Module,
86 qualifier: Option<ast::Path>, 120 qualifier: Option<ast::Path>,
87 fuzzy_name: String, 121 fuzzy_name: String,
88 sema: &Semantics<RootDatabase>, 122 sema: &Semantics<RootDatabase>,
123 candidate_node: SyntaxNode,
89 ) -> Option<Self> { 124 ) -> Option<Self> {
90 Some(match qualifier { 125 Some(Self {
91 Some(qualifier) => { 126 import_candidate: ImportCandidate::for_fuzzy_path(qualifier, fuzzy_name, sema)?,
92 let qualifier_resolution = sema.resolve_path(&qualifier)?; 127 module_with_candidate,
93 match qualifier_resolution { 128 candidate_node,
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 }) 129 })
118 } 130 }
119 131
120 pub fn for_fuzzy_method_call( 132 pub fn for_fuzzy_method_call(
121 module_with_method_call: Module, 133 module_with_method_call: Module,
122 receiver_ty: hir::Type, 134 receiver_ty: Type,
123 fuzzy_method_name: String, 135 fuzzy_method_name: String,
136 candidate_node: SyntaxNode,
124 ) -> Option<Self> { 137 ) -> Option<Self> {
125 Some(Self { 138 Some(Self {
126 import_candidate: ImportCandidate::TraitMethod(TraitImportCandidate { 139 import_candidate: ImportCandidate::TraitMethod(TraitImportCandidate {
127 receiver_ty, 140 receiver_ty,
128 name: NameToImport::Fuzzy(fuzzy_method_name), 141 assoc_item_name: NameToImport::Fuzzy(fuzzy_method_name),
129 }), 142 }),
130 module_with_candidate: module_with_method_call, 143 module_with_candidate: module_with_method_call,
144 candidate_node,
131 }) 145 })
132 } 146 }
133} 147}
134 148
149/// An import (not necessary the only one) that corresponds a certain given [`PathImportCandidate`].
150/// (the structure is not entirely correct, since there can be situations requiring two imports, see FIXME below for the details)
151#[derive(Debug, Clone, PartialEq, Eq, Hash)]
152pub struct LocatedImport {
153 /// The path to use in the `use` statement for a given candidate to be imported.
154 pub import_path: ModPath,
155 /// An item that will be imported with the import path given.
156 pub item_to_import: ItemInNs,
157 /// The path import candidate, resolved.
158 ///
159 /// Not necessary matches the import:
160 /// For any associated constant from the trait, we try to access as `some::path::SomeStruct::ASSOC_`
161 /// the original item is the associated constant, but the import has to be a trait that
162 /// defines this constant.
163 pub original_item: ItemInNs,
164 /// A path of the original item.
165 pub original_path: Option<ModPath>,
166}
167
168impl LocatedImport {
169 pub fn new(
170 import_path: ModPath,
171 item_to_import: ItemInNs,
172 original_item: ItemInNs,
173 original_path: Option<ModPath>,
174 ) -> Self {
175 Self { import_path, item_to_import, original_item, original_path }
176 }
177}
178
135impl ImportAssets { 179impl ImportAssets {
136 pub fn import_candidate(&self) -> &ImportCandidate { 180 pub fn import_candidate(&self) -> &ImportCandidate {
137 &self.import_candidate 181 &self.import_candidate
138 } 182 }
139 183
140 fn name_to_import(&self) -> &NameToImport {
141 match &self.import_candidate {
142 ImportCandidate::Path(candidate) => &candidate.name,
143 ImportCandidate::TraitAssocItem(candidate)
144 | ImportCandidate::TraitMethod(candidate) => &candidate.name,
145 }
146 }
147
148 pub fn search_for_imports( 184 pub fn search_for_imports(
149 &self, 185 &self,
150 sema: &Semantics<RootDatabase>, 186 sema: &Semantics<RootDatabase>,
151 prefix_kind: PrefixKind, 187 prefix_kind: PrefixKind,
152 ) -> Vec<(hir::ModPath, hir::ItemInNs)> { 188 ) -> Vec<LocatedImport> {
153 let _p = profile::span("import_assets::search_for_imports"); 189 let _p = profile::span("import_assets::search_for_imports");
154 self.search_for(sema, Some(prefix_kind)) 190 self.search_for(sema, Some(prefix_kind))
155 } 191 }
156 192
157 /// This may return non-absolute paths if a part of the returned path is already imported into scope. 193 /// This may return non-absolute paths if a part of the returned path is already imported into scope.
158 pub fn search_for_relative_paths( 194 pub fn search_for_relative_paths(&self, sema: &Semantics<RootDatabase>) -> Vec<LocatedImport> {
159 &self,
160 sema: &Semantics<RootDatabase>,
161 ) -> Vec<(hir::ModPath, hir::ItemInNs)> {
162 let _p = profile::span("import_assets::search_for_relative_paths"); 195 let _p = profile::span("import_assets::search_for_relative_paths");
163 self.search_for(sema, None) 196 self.search_for(sema, None)
164 } 197 }
@@ -166,29 +199,29 @@ impl ImportAssets {
166 fn search_for( 199 fn search_for(
167 &self, 200 &self,
168 sema: &Semantics<RootDatabase>, 201 sema: &Semantics<RootDatabase>,
169 prefixed: Option<hir::PrefixKind>, 202 prefixed: Option<PrefixKind>,
170 ) -> Vec<(hir::ModPath, hir::ItemInNs)> { 203 ) -> Vec<LocatedImport> {
171 let current_crate = self.module_with_candidate.krate(); 204 let items_with_candidate_name = match self.name_to_import() {
172 205 NameToImport::Exact(exact_name) => items_locator::with_exact_name(
173 let unfiltered_imports = match self.name_to_import() { 206 sema,
174 NameToImport::Exact(exact_name) => { 207 self.module_with_candidate.krate(),
175 imports_locator::find_exact_imports(sema, current_crate, exact_name.clone()) 208 exact_name.clone(),
176 } 209 ),
177 // FIXME: ideally, we should avoid using `fst` for seacrhing trait imports for assoc items: 210 // FIXME: ideally, we should avoid using `fst` for seacrhing trait imports for assoc items:
178 // instead, we need to look up all trait impls for a certain struct and search through them only 211 // instead, we need to look up all trait impls for a certain struct and search through them only
179 // see https://github.com/rust-analyzer/rust-analyzer/pull/7293#issuecomment-761585032 212 // see https://github.com/rust-analyzer/rust-analyzer/pull/7293#issuecomment-761585032
180 // and https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Blanket.20trait.20impls.20lookup 213 // and https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Blanket.20trait.20impls.20lookup
181 // for the details 214 // for the details
182 NameToImport::Fuzzy(fuzzy_name) => { 215 NameToImport::Fuzzy(fuzzy_name) => {
183 let (assoc_item_search, limit) = match self.import_candidate { 216 let (assoc_item_search, limit) = if self.import_candidate.is_trait_candidate() {
184 ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_) => { 217 (AssocItemSearch::AssocItemsOnly, None)
185 (AssocItemSearch::AssocItemsOnly, None) 218 } else {
186 } 219 (AssocItemSearch::Include, Some(DEFAULT_QUERY_SEARCH_LIMIT))
187 _ => (AssocItemSearch::Exclude, Some(DEFAULT_QUERY_SEARCH_LIMIT)),
188 }; 220 };
189 imports_locator::find_similar_imports( 221
222 items_locator::with_similar_name(
190 sema, 223 sema,
191 current_crate, 224 self.module_with_candidate.krate(),
192 fuzzy_name.clone(), 225 fuzzy_name.clone(),
193 assoc_item_search, 226 assoc_item_search,
194 limit, 227 limit,
@@ -196,63 +229,224 @@ impl ImportAssets {
196 } 229 }
197 }; 230 };
198 231
199 let db = sema.db; 232 let scope_definitions = self.scope_definitions(sema);
200 let mut res = 233 self.applicable_defs(sema.db, prefixed, items_with_candidate_name)
201 applicable_defs(self.import_candidate(), current_crate, db, unfiltered_imports) 234 .into_iter()
202 .filter_map(|candidate| { 235 .filter(|import| import.import_path.len() > 1)
203 let item: hir::ItemInNs = candidate.clone().either(Into::into, Into::into); 236 .filter(|import| !scope_definitions.contains(&ScopeDef::from(import.item_to_import)))
204 237 .sorted_by_key(|import| import.import_path.clone())
205 let item_to_search = match self.import_candidate { 238 .collect()
206 ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_) => { 239 }
207 let canidate_trait = match candidate { 240
208 Either::Left(module_def) => { 241 fn scope_definitions(&self, sema: &Semantics<RootDatabase>) -> FxHashSet<ScopeDef> {
209 module_def.as_assoc_item(db)?.containing_trait(db) 242 let mut scope_definitions = FxHashSet::default();
210 } 243 sema.scope(&self.candidate_node).process_all_names(&mut |_, scope_def| {
211 _ => None, 244 scope_definitions.insert(scope_def);
212 }?; 245 });
213 ModuleDef::from(canidate_trait).into() 246 scope_definitions
214 } 247 }
215 _ => item, 248
216 }; 249 fn name_to_import(&self) -> &NameToImport {
217 250 match &self.import_candidate {
218 if let Some(prefix_kind) = prefixed { 251 ImportCandidate::Path(candidate) => &candidate.name,
219 self.module_with_candidate.find_use_path_prefixed( 252 ImportCandidate::TraitAssocItem(candidate)
220 db, 253 | ImportCandidate::TraitMethod(candidate) => &candidate.assoc_item_name,
221 item_to_search, 254 }
222 prefix_kind, 255 }
223 ) 256
224 } else { 257 fn applicable_defs(
225 self.module_with_candidate.find_use_path(db, item_to_search) 258 &self,
226 } 259 db: &RootDatabase,
227 .map(|path| (path, item)) 260 prefixed: Option<PrefixKind>,
228 }) 261 items_with_candidate_name: FxHashSet<ItemInNs>,
229 .filter(|(use_path, _)| use_path.len() > 1) 262 ) -> FxHashSet<LocatedImport> {
230 .collect::<Vec<_>>(); 263 let _p = profile::span("import_assets::applicable_defs");
231 res.sort_by_cached_key(|(path, _)| path.clone()); 264 let current_crate = self.module_with_candidate.krate();
232 res 265
266 let mod_path = |item| {
267 get_mod_path(db, item_for_path_search(db, item)?, &self.module_with_candidate, prefixed)
268 };
269
270 match &self.import_candidate {
271 ImportCandidate::Path(path_candidate) => {
272 path_applicable_imports(db, path_candidate, mod_path, items_with_candidate_name)
273 }
274 ImportCandidate::TraitAssocItem(trait_candidate) => trait_applicable_items(
275 db,
276 current_crate,
277 trait_candidate,
278 true,
279 mod_path,
280 items_with_candidate_name,
281 ),
282 ImportCandidate::TraitMethod(trait_candidate) => trait_applicable_items(
283 db,
284 current_crate,
285 trait_candidate,
286 false,
287 mod_path,
288 items_with_candidate_name,
289 ),
290 }
233 } 291 }
234} 292}
235 293
236fn applicable_defs<'a>( 294fn path_applicable_imports(
237 import_candidate: &ImportCandidate,
238 current_crate: Crate,
239 db: &RootDatabase, 295 db: &RootDatabase,
240 unfiltered_imports: Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a>, 296 path_candidate: &PathImportCandidate,
241) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> { 297 mod_path: impl Fn(ItemInNs) -> Option<ModPath> + Copy,
242 let receiver_ty = match import_candidate { 298 items_with_candidate_name: FxHashSet<ItemInNs>,
243 ImportCandidate::Path(_) => return unfiltered_imports, 299) -> FxHashSet<LocatedImport> {
244 ImportCandidate::TraitAssocItem(candidate) | ImportCandidate::TraitMethod(candidate) => { 300 let _p = profile::span("import_assets::path_applicable_imports");
245 &candidate.receiver_ty 301
302 let (unresolved_first_segment, unresolved_qualifier) = match &path_candidate.qualifier {
303 None => {
304 return items_with_candidate_name
305 .into_iter()
306 .filter_map(|item| {
307 Some(LocatedImport::new(mod_path(item)?, item, item, mod_path(item)))
308 })
309 .collect();
246 } 310 }
311 Some(first_segment_unresolved) => (
312 first_segment_unresolved.fist_segment.to_string(),
313 path_to_string_stripping_turbo_fish(&first_segment_unresolved.full_qualifier),
314 ),
247 }; 315 };
248 316
317 items_with_candidate_name
318 .into_iter()
319 .filter_map(|item| {
320 import_for_item(db, mod_path, &unresolved_first_segment, &unresolved_qualifier, item)
321 })
322 .collect()
323}
324
325fn import_for_item(
326 db: &RootDatabase,
327 mod_path: impl Fn(ItemInNs) -> Option<ModPath>,
328 unresolved_first_segment: &str,
329 unresolved_qualifier: &str,
330 original_item: ItemInNs,
331) -> Option<LocatedImport> {
332 let _p = profile::span("import_assets::import_for_item");
333
334 let original_item_candidate = item_for_path_search(db, original_item)?;
335 let import_path_candidate = mod_path(original_item_candidate)?;
336 let import_path_string = import_path_candidate.to_string();
337
338 let expected_import_end = if item_as_assoc(db, original_item).is_some() {
339 unresolved_qualifier.to_string()
340 } else {
341 format!("{}::{}", unresolved_qualifier, item_name(db, original_item)?)
342 };
343 if !import_path_string.contains(unresolved_first_segment)
344 || !import_path_string.ends_with(&expected_import_end)
345 {
346 return None;
347 }
348
349 let segment_import =
350 find_import_for_segment(db, original_item_candidate, &unresolved_first_segment)?;
351 let trait_item_to_import = item_as_assoc(db, original_item)
352 .and_then(|assoc| assoc.containing_trait(db))
353 .map(|trait_| ItemInNs::from(ModuleDef::from(trait_)));
354 Some(match (segment_import == original_item_candidate, trait_item_to_import) {
355 (true, Some(_)) => {
356 // FIXME we should be able to import both the trait and the segment,
357 // but it's unclear what to do with overlapping edits (merge imports?)
358 // especially in case of lazy completion edit resolutions.
359 return None;
360 }
361 (false, Some(trait_to_import)) => LocatedImport::new(
362 mod_path(trait_to_import)?,
363 trait_to_import,
364 original_item,
365 mod_path(original_item),
366 ),
367 (true, None) => LocatedImport::new(
368 import_path_candidate,
369 original_item_candidate,
370 original_item,
371 mod_path(original_item),
372 ),
373 (false, None) => LocatedImport::new(
374 mod_path(segment_import)?,
375 segment_import,
376 original_item,
377 mod_path(original_item),
378 ),
379 })
380}
381
382fn item_for_path_search(db: &RootDatabase, item: ItemInNs) -> Option<ItemInNs> {
383 Some(match item {
384 ItemInNs::Types(_) | ItemInNs::Values(_) => match item_as_assoc(db, item) {
385 Some(assoc_item) => match assoc_item.container(db) {
386 AssocItemContainer::Trait(trait_) => ItemInNs::from(ModuleDef::from(trait_)),
387 AssocItemContainer::Impl(impl_) => {
388 ItemInNs::from(ModuleDef::from(impl_.target_ty(db).as_adt()?))
389 }
390 },
391 None => item,
392 },
393 ItemInNs::Macros(_) => item,
394 })
395}
396
397fn find_import_for_segment(
398 db: &RootDatabase,
399 original_item: ItemInNs,
400 unresolved_first_segment: &str,
401) -> Option<ItemInNs> {
402 let segment_is_name = item_name(db, original_item)
403 .map(|name| name.to_string() == unresolved_first_segment)
404 .unwrap_or(false);
405
406 Some(if segment_is_name {
407 original_item
408 } else {
409 let matching_module =
410 module_with_segment_name(db, &unresolved_first_segment, original_item)?;
411 ItemInNs::from(ModuleDef::from(matching_module))
412 })
413}
414
415fn module_with_segment_name(
416 db: &RootDatabase,
417 segment_name: &str,
418 candidate: ItemInNs,
419) -> Option<Module> {
420 let mut current_module = match candidate {
421 ItemInNs::Types(module_def_id) => ModuleDef::from(module_def_id).module(db),
422 ItemInNs::Values(module_def_id) => ModuleDef::from(module_def_id).module(db),
423 ItemInNs::Macros(macro_def_id) => MacroDef::from(macro_def_id).module(db),
424 };
425 while let Some(module) = current_module {
426 if let Some(module_name) = module.name(db) {
427 if module_name.to_string() == segment_name {
428 return Some(module);
429 }
430 }
431 current_module = module.parent(db);
432 }
433 None
434}
435
436fn trait_applicable_items(
437 db: &RootDatabase,
438 current_crate: Crate,
439 trait_candidate: &TraitImportCandidate,
440 trait_assoc_item: bool,
441 mod_path: impl Fn(ItemInNs) -> Option<ModPath>,
442 items_with_candidate_name: FxHashSet<ItemInNs>,
443) -> FxHashSet<LocatedImport> {
444 let _p = profile::span("import_assets::trait_applicable_items");
249 let mut required_assoc_items = FxHashSet::default(); 445 let mut required_assoc_items = FxHashSet::default();
250 446
251 let trait_candidates = unfiltered_imports 447 let trait_candidates = items_with_candidate_name
252 .filter_map(|input| match input { 448 .into_iter()
253 Either::Left(module_def) => module_def.as_assoc_item(db), 449 .filter_map(|input| item_as_assoc(db, input))
254 _ => None,
255 })
256 .filter_map(|assoc| { 450 .filter_map(|assoc| {
257 let assoc_item_trait = assoc.containing_trait(db)?; 451 let assoc_item_trait = assoc.containing_trait(db)?;
258 required_assoc_items.insert(assoc); 452 required_assoc_items.insert(assoc);
@@ -260,11 +454,10 @@ fn applicable_defs<'a>(
260 }) 454 })
261 .collect(); 455 .collect();
262 456
263 let mut applicable_defs = FxHashSet::default(); 457 let mut located_imports = FxHashSet::default();
264 458
265 match import_candidate { 459 if trait_assoc_item {
266 ImportCandidate::Path(_) => unreachable!(), 460 trait_candidate.receiver_ty.iterate_path_candidates(
267 ImportCandidate::TraitAssocItem(_) => receiver_ty.iterate_path_candidates(
268 db, 461 db,
269 current_crate, 462 current_crate,
270 &trait_candidates, 463 &trait_candidates,
@@ -276,12 +469,21 @@ fn applicable_defs<'a>(
276 return None; 469 return None;
277 } 470 }
278 } 471 }
279 applicable_defs.insert(Either::Left(assoc_to_module_def(assoc))); 472
473 let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?));
474 let original_item = assoc_to_item(assoc);
475 located_imports.insert(LocatedImport::new(
476 mod_path(item)?,
477 item,
478 original_item,
479 mod_path(original_item),
480 ));
280 } 481 }
281 None::<()> 482 None::<()>
282 }, 483 },
283 ), 484 )
284 ImportCandidate::TraitMethod(_) => receiver_ty.iterate_method_candidates( 485 } else {
486 trait_candidate.receiver_ty.iterate_method_candidates(
285 db, 487 db,
286 current_crate, 488 current_crate,
287 &trait_candidates, 489 &trait_candidates,
@@ -289,21 +491,41 @@ fn applicable_defs<'a>(
289 |_, function| { 491 |_, function| {
290 let assoc = function.as_assoc_item(db)?; 492 let assoc = function.as_assoc_item(db)?;
291 if required_assoc_items.contains(&assoc) { 493 if required_assoc_items.contains(&assoc) {
292 applicable_defs.insert(Either::Left(assoc_to_module_def(assoc))); 494 let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?));
495 let original_item = assoc_to_item(assoc);
496 located_imports.insert(LocatedImport::new(
497 mod_path(item)?,
498 item,
499 original_item,
500 mod_path(original_item),
501 ));
293 } 502 }
294 None::<()> 503 None::<()>
295 }, 504 },
296 ), 505 )
297 }; 506 };
298 507
299 Box::new(applicable_defs.into_iter()) 508 located_imports
300} 509}
301 510
302fn assoc_to_module_def(assoc: AssocItem) -> ModuleDef { 511fn assoc_to_item(assoc: AssocItem) -> ItemInNs {
303 match assoc { 512 match assoc {
304 AssocItem::Function(f) => f.into(), 513 AssocItem::Function(f) => ItemInNs::from(ModuleDef::from(f)),
305 AssocItem::Const(c) => c.into(), 514 AssocItem::Const(c) => ItemInNs::from(ModuleDef::from(c)),
306 AssocItem::TypeAlias(t) => t.into(), 515 AssocItem::TypeAlias(t) => ItemInNs::from(ModuleDef::from(t)),
516 }
517}
518
519fn get_mod_path(
520 db: &RootDatabase,
521 item_to_search: ItemInNs,
522 module_with_candidate: &Module,
523 prefixed: Option<PrefixKind>,
524) -> Option<ModPath> {
525 if let Some(prefix_kind) = prefixed {
526 module_with_candidate.find_use_path_prefixed(db, item_to_search, prefix_kind)
527 } else {
528 module_with_candidate.find_use_path(db, item_to_search)
307 } 529 }
308} 530}
309 531
@@ -316,7 +538,7 @@ impl ImportCandidate {
316 Some(_) => None, 538 Some(_) => None,
317 None => Some(Self::TraitMethod(TraitImportCandidate { 539 None => Some(Self::TraitMethod(TraitImportCandidate {
318 receiver_ty: sema.type_of_expr(&method_call.receiver()?)?, 540 receiver_ty: sema.type_of_expr(&method_call.receiver()?)?,
319 name: NameToImport::Exact(method_call.name_ref()?.to_string()), 541 assoc_item_name: NameToImport::Exact(method_call.name_ref()?.to_string()),
320 })), 542 })),
321 } 543 }
322 } 544 }
@@ -325,41 +547,63 @@ impl ImportCandidate {
325 if sema.resolve_path(path).is_some() { 547 if sema.resolve_path(path).is_some() {
326 return None; 548 return None;
327 } 549 }
550 path_import_candidate(
551 sema,
552 path.qualifier(),
553 NameToImport::Exact(path.segment()?.name_ref()?.to_string()),
554 )
555 }
328 556
329 let segment = path.segment()?; 557 fn for_fuzzy_path(
330 let candidate = if let Some(qualifier) = path.qualifier() { 558 qualifier: Option<ast::Path>,
331 let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?; 559 fuzzy_name: String,
332 let qualifier_start_path = 560 sema: &Semantics<RootDatabase>,
333 qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; 561 ) -> Option<Self> {
334 if let Some(qualifier_start_resolution) = sema.resolve_path(&qualifier_start_path) { 562 path_import_candidate(sema, qualifier, NameToImport::Fuzzy(fuzzy_name))
335 let qualifier_resolution = if qualifier_start_path == qualifier { 563 }
336 qualifier_start_resolution 564
565 fn is_trait_candidate(&self) -> bool {
566 matches!(self, ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_))
567 }
568}
569
570fn path_import_candidate(
571 sema: &Semantics<RootDatabase>,
572 qualifier: Option<ast::Path>,
573 name: NameToImport,
574) -> Option<ImportCandidate> {
575 Some(match qualifier {
576 Some(qualifier) => match sema.resolve_path(&qualifier) {
577 None => {
578 let qualifier_start =
579 qualifier.syntax().descendants().find_map(ast::NameRef::cast)?;
580 let qualifier_start_path =
581 qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?;
582 if sema.resolve_path(&qualifier_start_path).is_none() {
583 ImportCandidate::Path(PathImportCandidate {
584 qualifier: Some(FirstSegmentUnresolved {
585 fist_segment: qualifier_start,
586 full_qualifier: qualifier,
587 }),
588 name,
589 })
337 } else { 590 } else {
338 sema.resolve_path(&qualifier)? 591 return None;
339 };
340 match qualifier_resolution {
341 hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => {
342 ImportCandidate::TraitAssocItem(TraitImportCandidate {
343 receiver_ty: assoc_item_path.ty(sema.db),
344 name: NameToImport::Exact(segment.name_ref()?.to_string()),
345 })
346 }
347 _ => return None,
348 } 592 }
349 } else { 593 }
350 ImportCandidate::Path(PathImportCandidate { 594 Some(PathResolution::Def(ModuleDef::Adt(assoc_item_path))) => {
351 qualifier: Some(qualifier), 595 ImportCandidate::TraitAssocItem(TraitImportCandidate {
352 name: NameToImport::Exact(qualifier_start.to_string()), 596 receiver_ty: assoc_item_path.ty(sema.db),
597 assoc_item_name: name,
353 }) 598 })
354 } 599 }
355 } else { 600 Some(_) => return None,
356 ImportCandidate::Path(PathImportCandidate { 601 },
357 qualifier: None, 602 None => ImportCandidate::Path(PathImportCandidate { qualifier: None, name }),
358 name: NameToImport::Exact( 603 })
359 segment.syntax().descendants().find_map(ast::NameRef::cast)?.to_string(), 604}
360 ), 605
361 }) 606fn item_as_assoc(db: &RootDatabase, item: ItemInNs) -> Option<AssocItem> {
362 }; 607 item.as_module_def_id()
363 Some(candidate) 608 .and_then(|module_def_id| ModuleDef::from(module_def_id).as_assoc_item(db))
364 }
365} 609}
diff --git a/crates/ide_db/src/imports_locator.rs b/crates/ide_db/src/items_locator.rs
index 502e8281a..8a7f02935 100644
--- a/crates/ide_db/src/imports_locator.rs
+++ b/crates/ide_db/src/items_locator.rs
@@ -1,9 +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
4use either::Either;
4use hir::{ 5use hir::{
5 import_map::{self, ImportKind}, 6 import_map::{self, ImportKind},
6 AsAssocItem, Crate, MacroDef, ModuleDef, Semantics, 7 AsAssocItem, Crate, ItemInNs, ModuleDef, Semantics,
7}; 8};
8use syntax::{ast, AstNode, SyntaxKind::NAME}; 9use syntax::{ast, AstNode, SyntaxKind::NAME};
9 10
@@ -12,47 +13,47 @@ use crate::{
12 symbol_index::{self, FileSymbol}, 13 symbol_index::{self, FileSymbol},
13 RootDatabase, 14 RootDatabase,
14}; 15};
15use either::Either;
16use rustc_hash::FxHashSet; 16use rustc_hash::FxHashSet;
17 17
18pub(crate) const DEFAULT_QUERY_SEARCH_LIMIT: usize = 40; 18pub(crate) const DEFAULT_QUERY_SEARCH_LIMIT: usize = 40;
19 19
20pub fn find_exact_imports<'a>( 20pub fn with_exact_name(
21 sema: &Semantics<'a, RootDatabase>, 21 sema: &Semantics<'_, RootDatabase>,
22 krate: Crate, 22 krate: Crate,
23 name_to_import: String, 23 exact_name: String,
24) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>>> { 24) -> FxHashSet<ItemInNs> {
25 let _p = profile::span("find_exact_imports"); 25 let _p = profile::span("find_exact_imports");
26 Box::new(find_imports( 26 find_items(
27 sema, 27 sema,
28 krate, 28 krate,
29 { 29 {
30 let mut local_query = symbol_index::Query::new(name_to_import.clone()); 30 let mut local_query = symbol_index::Query::new(exact_name.clone());
31 local_query.exact(); 31 local_query.exact();
32 local_query.limit(DEFAULT_QUERY_SEARCH_LIMIT); 32 local_query.limit(DEFAULT_QUERY_SEARCH_LIMIT);
33 local_query 33 local_query
34 }, 34 },
35 import_map::Query::new(name_to_import) 35 import_map::Query::new(exact_name)
36 .limit(DEFAULT_QUERY_SEARCH_LIMIT) 36 .limit(DEFAULT_QUERY_SEARCH_LIMIT)
37 .name_only() 37 .name_only()
38 .search_mode(import_map::SearchMode::Equals) 38 .search_mode(import_map::SearchMode::Equals)
39 .case_sensitive(), 39 .case_sensitive(),
40 )) 40 )
41} 41}
42 42
43#[derive(Debug)]
43pub enum AssocItemSearch { 44pub enum AssocItemSearch {
44 Include, 45 Include,
45 Exclude, 46 Exclude,
46 AssocItemsOnly, 47 AssocItemsOnly,
47} 48}
48 49
49pub fn find_similar_imports<'a>( 50pub fn with_similar_name(
50 sema: &Semantics<'a, RootDatabase>, 51 sema: &Semantics<'_, RootDatabase>,
51 krate: Crate, 52 krate: Crate,
52 fuzzy_search_string: String, 53 fuzzy_search_string: String,
53 assoc_item_search: AssocItemSearch, 54 assoc_item_search: AssocItemSearch,
54 limit: Option<usize>, 55 limit: Option<usize>,
55) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> { 56) -> FxHashSet<ItemInNs> {
56 let _p = profile::span("find_similar_imports"); 57 let _p = profile::span("find_similar_imports");
57 58
58 let mut external_query = import_map::Query::new(fuzzy_search_string.clone()) 59 let mut external_query = import_map::Query::new(fuzzy_search_string.clone())
@@ -76,37 +77,39 @@ pub fn find_similar_imports<'a>(
76 local_query.limit(limit); 77 local_query.limit(limit);
77 } 78 }
78 79
79 let db = sema.db; 80 find_items(sema, krate, local_query, external_query)
80 Box::new(find_imports(sema, krate, local_query, external_query).filter( 81 .into_iter()
81 move |import_candidate| match assoc_item_search { 82 .filter(move |&item| match assoc_item_search {
82 AssocItemSearch::Include => true, 83 AssocItemSearch::Include => true,
83 AssocItemSearch::Exclude => !is_assoc_item(import_candidate, db), 84 AssocItemSearch::Exclude => !is_assoc_item(item, sema.db),
84 AssocItemSearch::AssocItemsOnly => is_assoc_item(import_candidate, db), 85 AssocItemSearch::AssocItemsOnly => is_assoc_item(item, sema.db),
85 }, 86 })
86 )) 87 .collect()
87} 88}
88 89
89fn is_assoc_item(import_candidate: &Either<ModuleDef, MacroDef>, db: &RootDatabase) -> bool { 90fn is_assoc_item(item: ItemInNs, db: &RootDatabase) -> bool {
90 match import_candidate { 91 item.as_module_def_id()
91 Either::Left(ModuleDef::Function(function)) => function.as_assoc_item(db).is_some(), 92 .and_then(|module_def_id| ModuleDef::from(module_def_id).as_assoc_item(db))
92 Either::Left(ModuleDef::Const(const_)) => const_.as_assoc_item(db).is_some(), 93 .is_some()
93 Either::Left(ModuleDef::TypeAlias(type_alias)) => type_alias.as_assoc_item(db).is_some(),
94 _ => false,
95 }
96} 94}
97 95
98fn find_imports<'a>( 96fn find_items(
99 sema: &Semantics<'a, RootDatabase>, 97 sema: &Semantics<'_, RootDatabase>,
100 krate: Crate, 98 krate: Crate,
101 local_query: symbol_index::Query, 99 local_query: symbol_index::Query,
102 external_query: import_map::Query, 100 external_query: import_map::Query,
103) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { 101) -> FxHashSet<ItemInNs> {
104 let _p = profile::span("find_similar_imports"); 102 let _p = profile::span("find_similar_imports");
105 let db = sema.db; 103 let db = sema.db;
106 104
107 // Query dependencies first. 105 // Query dependencies first.
108 let mut candidates: FxHashSet<_> = 106 let mut candidates = krate
109 krate.query_external_importables(db, external_query).collect(); 107 .query_external_importables(db, external_query)
108 .map(|external_importable| match external_importable {
109 Either::Left(module_def) => ItemInNs::from(module_def),
110 Either::Right(macro_def) => ItemInNs::from(macro_def),
111 })
112 .collect::<FxHashSet<_>>();
110 113
111 // Query the local crate using the symbol index. 114 // Query the local crate using the symbol index.
112 let local_results = symbol_index::crate_symbols(db, krate.into(), local_query); 115 let local_results = symbol_index::crate_symbols(db, krate.into(), local_query);
@@ -114,19 +117,19 @@ fn find_imports<'a>(
114 candidates.extend( 117 candidates.extend(
115 local_results 118 local_results
116 .into_iter() 119 .into_iter()
117 .filter_map(|import_candidate| get_name_definition(sema, &import_candidate)) 120 .filter_map(|local_candidate| get_name_definition(sema, &local_candidate))
118 .filter_map(|name_definition_to_import| match name_definition_to_import { 121 .filter_map(|name_definition_to_import| match name_definition_to_import {
119 Definition::ModuleDef(module_def) => Some(Either::Left(module_def)), 122 Definition::ModuleDef(module_def) => Some(ItemInNs::from(module_def)),
120 Definition::Macro(macro_def) => Some(Either::Right(macro_def)), 123 Definition::Macro(macro_def) => Some(ItemInNs::from(macro_def)),
121 _ => None, 124 _ => None,
122 }), 125 }),
123 ); 126 );
124 127
125 candidates.into_iter() 128 candidates
126} 129}
127 130
128fn get_name_definition<'a>( 131fn get_name_definition(
129 sema: &Semantics<'a, RootDatabase>, 132 sema: &Semantics<'_, RootDatabase>,
130 import_candidate: &FileSymbol, 133 import_candidate: &FileSymbol,
131) -> Option<Definition> { 134) -> Option<Definition> {
132 let _p = profile::span("get_name_definition"); 135 let _p = profile::span("get_name_definition");
diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs
index 6eb34b06b..88ee4a87d 100644
--- a/crates/ide_db/src/lib.rs
+++ b/crates/ide_db/src/lib.rs
@@ -8,7 +8,7 @@ pub mod line_index;
8pub mod symbol_index; 8pub mod symbol_index;
9pub mod defs; 9pub mod defs;
10pub mod search; 10pub mod search;
11pub mod imports_locator; 11pub mod items_locator;
12pub mod source_change; 12pub mod source_change;
13pub mod ty_filter; 13pub mod ty_filter;
14pub mod traits; 14pub mod traits;
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 28e221271..25df13554 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -16,7 +16,6 @@ use ide_db::helpers::{
16 insert_use::{InsertUseConfig, MergeBehavior}, 16 insert_use::{InsertUseConfig, MergeBehavior},
17 SnippetCap, 17 SnippetCap,
18}; 18};
19use itertools::Itertools;
20use lsp_types::{ClientCapabilities, MarkupKind}; 19use lsp_types::{ClientCapabilities, MarkupKind};
21use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource}; 20use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource};
22use rustc_hash::FxHashSet; 21use rustc_hash::FxHashSet;
@@ -98,13 +97,15 @@ config_data! {
98 diagnostics_enableExperimental: bool = "true", 97 diagnostics_enableExperimental: bool = "true",
99 /// List of rust-analyzer diagnostics to disable. 98 /// List of rust-analyzer diagnostics to disable.
100 diagnostics_disabled: FxHashSet<String> = "[]", 99 diagnostics_disabled: FxHashSet<String> = "[]",
101 /// List of warnings that should be displayed with info severity.\n\nThe 100 /// List of warnings that should be displayed with info severity.
102 /// warnings will be indicated by a blue squiggly underline in code and 101 ///
103 /// a blue icon in the `Problems Panel`. 102 /// The warnings will be indicated by a blue squiggly underline in code
103 /// and a blue icon in the `Problems Panel`.
104 diagnostics_warningsAsHint: Vec<String> = "[]", 104 diagnostics_warningsAsHint: Vec<String> = "[]",
105 /// List of warnings that should be displayed with hint severity.\n\nThe 105 /// List of warnings that should be displayed with hint severity.
106 /// warnings will be indicated by faded text or three dots in code and 106 ///
107 /// will not show up in the `Problems Panel`. 107 /// The warnings will be indicated by faded text or three dots in code
108 /// and will not show up in the `Problems Panel`.
108 diagnostics_warningsAsInfo: Vec<String> = "[]", 109 diagnostics_warningsAsInfo: Vec<String> = "[]",
109 110
110 /// Controls file watching implementation. 111 /// Controls file watching implementation.
@@ -158,7 +159,9 @@ config_data! {
158 lens_references: bool = "false", 159 lens_references: bool = "false",
159 160
160 /// Disable project auto-discovery in favor of explicitly specified set 161 /// Disable project auto-discovery in favor of explicitly specified set
161 /// of projects.\n\nElements must be paths pointing to `Cargo.toml`, 162 /// of projects.
163 ///
164 /// Elements must be paths pointing to `Cargo.toml`,
162 /// `rust-project.json`, or JSON objects in `rust-project.json` format. 165 /// `rust-project.json`, or JSON objects in `rust-project.json` format.
163 linkedProjects: Vec<ManifestOrProjectJson> = "[]", 166 linkedProjects: Vec<ManifestOrProjectJson> = "[]",
164 167
@@ -177,7 +180,7 @@ config_data! {
177 /// Command to be executed instead of 'cargo' for runnables. 180 /// Command to be executed instead of 'cargo' for runnables.
178 runnables_overrideCargo: Option<String> = "null", 181 runnables_overrideCargo: Option<String> = "null",
179 /// Additional arguments to be passed to cargo for runnables such as 182 /// Additional arguments to be passed to cargo for runnables such as
180 /// tests or binaries.\nFor example, it may be `--release`. 183 /// tests or binaries. For example, it may be `--release`.
181 runnables_cargoExtraArgs: Vec<String> = "[]", 184 runnables_cargoExtraArgs: Vec<String> = "[]",
182 185
183 /// Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private 186 /// Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private
@@ -765,7 +768,8 @@ fn schema(fields: &[(&'static str, &'static str, &[&str], &str)]) -> serde_json:
765} 768}
766 769
767fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json::Value { 770fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json::Value {
768 let doc = doc.iter().map(|it| it.trim()).join(" "); 771 let doc = doc_comment_to_string(doc);
772 let doc = doc.trim_end_matches('\n');
769 assert!( 773 assert!(
770 doc.ends_with('.') && doc.starts_with(char::is_uppercase), 774 doc.ends_with('.') && doc.starts_with(char::is_uppercase),
771 "bad docs for {}: {:?}", 775 "bad docs for {}: {:?}",
@@ -854,11 +858,16 @@ fn manual(fields: &[(&'static str, &'static str, &[&str], &str)]) -> String {
854 .iter() 858 .iter()
855 .map(|(field, _ty, doc, default)| { 859 .map(|(field, _ty, doc, default)| {
856 let name = format!("rust-analyzer.{}", field.replace("_", ".")); 860 let name = format!("rust-analyzer.{}", field.replace("_", "."));
857 format!("[[{}]]{} (default: `{}`)::\n{}\n", name, name, default, doc.join(" ")) 861 let doc = doc_comment_to_string(*doc);
862 format!("[[{}]]{} (default: `{}`)::\n+\n--\n{}--\n", name, name, default, doc)
858 }) 863 })
859 .collect::<String>() 864 .collect::<String>()
860} 865}
861 866
867fn doc_comment_to_string(doc: &[&str]) -> String {
868 doc.iter().map(|it| it.strip_prefix(' ').unwrap_or(it)).map(|it| format!("{}\n", it)).collect()
869}
870
862#[cfg(test)] 871#[cfg(test)]
863mod tests { 872mod tests {
864 use std::fs; 873 use std::fs;
@@ -901,13 +910,8 @@ mod tests {
901 #[test] 910 #[test]
902 fn generate_config_documentation() { 911 fn generate_config_documentation() {
903 let docs_path = project_root().join("docs/user/generated_config.adoc"); 912 let docs_path = project_root().join("docs/user/generated_config.adoc");
904 let current = fs::read_to_string(&docs_path).unwrap();
905 let expected = ConfigData::manual(); 913 let expected = ConfigData::manual();
906 914 ensure_file_contents(&docs_path, &expected);
907 if remove_ws(&current) != remove_ws(&expected) {
908 fs::write(&docs_path, expected).unwrap();
909 panic!("updated config manual");
910 }
911 } 915 }
912 916
913 fn remove_ws(text: &str) -> String { 917 fn remove_ws(text: &str) -> String {
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 4f6f250d6..2c4c339cb 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -697,7 +697,6 @@ pub(crate) fn handle_completion_resolve(
697 FilePosition { file_id, offset }, 697 FilePosition { file_id, offset },
698 &resolve_data.full_import_path, 698 &resolve_data.full_import_path,
699 resolve_data.imported_name, 699 resolve_data.imported_name,
700 resolve_data.import_for_trait_assoc_item,
701 )? 700 )?
702 .into_iter() 701 .into_iter()
703 .flat_map(|edit| edit.into_iter().map(|indel| to_proto::text_edit(&line_index, indel))) 702 .flat_map(|edit| edit.into_iter().map(|indel| to_proto::text_edit(&line_index, indel)))
@@ -1525,7 +1524,6 @@ struct CompletionResolveData {
1525 position: lsp_types::TextDocumentPositionParams, 1524 position: lsp_types::TextDocumentPositionParams,
1526 full_import_path: String, 1525 full_import_path: String,
1527 imported_name: String, 1526 imported_name: String,
1528 import_for_trait_assoc_item: bool,
1529} 1527}
1530 1528
1531fn fill_resolve_data( 1529fn fill_resolve_data(
@@ -1534,15 +1532,13 @@ fn fill_resolve_data(
1534 position: &TextDocumentPositionParams, 1532 position: &TextDocumentPositionParams,
1535) -> Option<()> { 1533) -> Option<()> {
1536 let import_edit = item.import_to_add()?; 1534 let import_edit = item.import_to_add()?;
1537 let full_import_path = import_edit.import_path.to_string(); 1535 let import_path = &import_edit.import.import_path;
1538 let imported_name = import_edit.import_path.segments().last()?.to_string();
1539 1536
1540 *resolve_data = Some( 1537 *resolve_data = Some(
1541 to_value(CompletionResolveData { 1538 to_value(CompletionResolveData {
1542 position: position.to_owned(), 1539 position: position.to_owned(),
1543 full_import_path, 1540 full_import_path: import_path.to_string(),
1544 imported_name, 1541 imported_name: import_path.segments().last()?.to_string(),
1545 import_for_trait_assoc_item: import_edit.import_for_trait_assoc_item,
1546 }) 1542 })
1547 .unwrap(), 1543 .unwrap(),
1548 ); 1544 );
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index b6c5de658..70ba8adb4 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -91,6 +91,10 @@ pub fn path_from_segments(
91 }) 91 })
92} 92}
93 93
94pub fn path_from_text(text: &str) -> ast::Path {
95 ast_from_text(&format!("fn main() {{ let test = {}; }}", text))
96}
97
94pub fn glob_use_tree() -> ast::UseTree { 98pub fn glob_use_tree() -> ast::UseTree {
95 ast_from_text("use *;") 99 ast_from_text("use *;")
96} 100}
diff --git a/crates/syntax/src/lib.rs b/crates/syntax/src/lib.rs
index 11294c5b2..09e212e8c 100644
--- a/crates/syntax/src/lib.rs
+++ b/crates/syntax/src/lib.rs
@@ -37,6 +37,7 @@ pub mod algo;
37pub mod ast; 37pub mod ast;
38#[doc(hidden)] 38#[doc(hidden)]
39pub mod fuzz; 39pub mod fuzz;
40pub mod utils;
40 41
41use std::{marker::PhantomData, sync::Arc}; 42use std::{marker::PhantomData, sync::Arc};
42 43
diff --git a/crates/syntax/src/utils.rs b/crates/syntax/src/utils.rs
new file mode 100644
index 000000000..f4c02518b
--- /dev/null
+++ b/crates/syntax/src/utils.rs
@@ -0,0 +1,43 @@
1//! A set of utils methods to reuse on other abstraction levels
2
3use itertools::Itertools;
4
5use crate::{ast, match_ast, AstNode};
6
7pub fn path_to_string_stripping_turbo_fish(path: &ast::Path) -> String {
8 path.syntax()
9 .children()
10 .filter_map(|node| {
11 match_ast! {
12 match node {
13 ast::PathSegment(it) => {
14 Some(it.name_ref()?.to_string())
15 },
16 ast::Path(it) => {
17 Some(path_to_string_stripping_turbo_fish(&it))
18 },
19 _ => None,
20 }
21 }
22 })
23 .join("::")
24}
25
26#[cfg(test)]
27mod tests {
28 use super::path_to_string_stripping_turbo_fish;
29 use crate::ast::make;
30
31 #[test]
32 fn turbofishes_are_stripped() {
33 assert_eq!("Vec", path_to_string_stripping_turbo_fish(&make::path_from_text("Vec::<i32>")),);
34 assert_eq!(
35 "Vec::new",
36 path_to_string_stripping_turbo_fish(&make::path_from_text("Vec::<i32>::new")),
37 );
38 assert_eq!(
39 "Vec::new",
40 path_to_string_stripping_turbo_fish(&make::path_from_text("Vec::new()")),
41 );
42 }
43}
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index e0ffcc484..042ba2d54 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -1,114 +1,322 @@
1[[rust-analyzer.assist.importMergeBehavior]]rust-analyzer.assist.importMergeBehavior (default: `"full"`):: 1[[rust-analyzer.assist.importMergeBehavior]]rust-analyzer.assist.importMergeBehavior (default: `"full"`)::
2 The strategy to use when inserting new imports or merging imports. 2+
3--
4The strategy to use when inserting new imports or merging imports.
5--
3[[rust-analyzer.assist.importPrefix]]rust-analyzer.assist.importPrefix (default: `"plain"`):: 6[[rust-analyzer.assist.importPrefix]]rust-analyzer.assist.importPrefix (default: `"plain"`)::
4 The path structure for newly inserted paths to use. 7+
8--
9The path structure for newly inserted paths to use.
10--
5[[rust-analyzer.assist.importGroup]]rust-analyzer.assist.importGroup (default: `true`):: 11[[rust-analyzer.assist.importGroup]]rust-analyzer.assist.importGroup (default: `true`)::
6 Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines. 12+
13--
14Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines.
15--
7[[rust-analyzer.callInfo.full]]rust-analyzer.callInfo.full (default: `true`):: 16[[rust-analyzer.callInfo.full]]rust-analyzer.callInfo.full (default: `true`)::
8 Show function name and docs in parameter hints. 17+
18--
19Show function name and docs in parameter hints.
20--
9[[rust-analyzer.cargo.autoreload]]rust-analyzer.cargo.autoreload (default: `true`):: 21[[rust-analyzer.cargo.autoreload]]rust-analyzer.cargo.autoreload (default: `true`)::
10 Automatically refresh project info via `cargo metadata` on `Cargo.toml` changes. 22+
23--
24Automatically refresh project info via `cargo metadata` on
25`Cargo.toml` changes.
26--
11[[rust-analyzer.cargo.allFeatures]]rust-analyzer.cargo.allFeatures (default: `false`):: 27[[rust-analyzer.cargo.allFeatures]]rust-analyzer.cargo.allFeatures (default: `false`)::
12 Activate all available features (`--all-features`). 28+
29--
30Activate all available features (`--all-features`).
31--
13[[rust-analyzer.cargo.features]]rust-analyzer.cargo.features (default: `[]`):: 32[[rust-analyzer.cargo.features]]rust-analyzer.cargo.features (default: `[]`)::
14 List of features to activate. 33+
34--
35List of features to activate.
36--
15[[rust-analyzer.cargo.runBuildScripts]]rust-analyzer.cargo.runBuildScripts (default: `true`):: 37[[rust-analyzer.cargo.runBuildScripts]]rust-analyzer.cargo.runBuildScripts (default: `true`)::
16 Run build scripts (`build.rs`) for more precise code analysis. 38+
39--
40Run build scripts (`build.rs`) for more precise code analysis.
41--
17[[rust-analyzer.cargo.noDefaultFeatures]]rust-analyzer.cargo.noDefaultFeatures (default: `false`):: 42[[rust-analyzer.cargo.noDefaultFeatures]]rust-analyzer.cargo.noDefaultFeatures (default: `false`)::
18 Do not activate the `default` feature. 43+
44--
45Do not activate the `default` feature.
46--
19[[rust-analyzer.cargo.target]]rust-analyzer.cargo.target (default: `null`):: 47[[rust-analyzer.cargo.target]]rust-analyzer.cargo.target (default: `null`)::
20 Compilation target (target triple). 48+
49--
50Compilation target (target triple).
51--
21[[rust-analyzer.cargo.noSysroot]]rust-analyzer.cargo.noSysroot (default: `false`):: 52[[rust-analyzer.cargo.noSysroot]]rust-analyzer.cargo.noSysroot (default: `false`)::
22 Internal config for debugging, disables loading of sysroot crates. 53+
54--
55Internal config for debugging, disables loading of sysroot crates.
56--
23[[rust-analyzer.checkOnSave.enable]]rust-analyzer.checkOnSave.enable (default: `true`):: 57[[rust-analyzer.checkOnSave.enable]]rust-analyzer.checkOnSave.enable (default: `true`)::
24 Run specified `cargo check` command for diagnostics on save. 58+
59--
60Run specified `cargo check` command for diagnostics on save.
61--
25[[rust-analyzer.checkOnSave.allFeatures]]rust-analyzer.checkOnSave.allFeatures (default: `null`):: 62[[rust-analyzer.checkOnSave.allFeatures]]rust-analyzer.checkOnSave.allFeatures (default: `null`)::
26 Check with all features (`--all-features`). Defaults to `#rust-analyzer.cargo.allFeatures#`. 63+
64--
65Check with all features (`--all-features`).
66Defaults to `#rust-analyzer.cargo.allFeatures#`.
67--
27[[rust-analyzer.checkOnSave.allTargets]]rust-analyzer.checkOnSave.allTargets (default: `true`):: 68[[rust-analyzer.checkOnSave.allTargets]]rust-analyzer.checkOnSave.allTargets (default: `true`)::
28 Check all targets and tests (`--all-targets`). 69+
70--
71Check all targets and tests (`--all-targets`).
72--
29[[rust-analyzer.checkOnSave.command]]rust-analyzer.checkOnSave.command (default: `"check"`):: 73[[rust-analyzer.checkOnSave.command]]rust-analyzer.checkOnSave.command (default: `"check"`)::
30 Cargo command to use for `cargo check`. 74+
75--
76Cargo command to use for `cargo check`.
77--
31[[rust-analyzer.checkOnSave.noDefaultFeatures]]rust-analyzer.checkOnSave.noDefaultFeatures (default: `null`):: 78[[rust-analyzer.checkOnSave.noDefaultFeatures]]rust-analyzer.checkOnSave.noDefaultFeatures (default: `null`)::
32 Do not activate the `default` feature. 79+
80--
81Do not activate the `default` feature.
82--
33[[rust-analyzer.checkOnSave.target]]rust-analyzer.checkOnSave.target (default: `null`):: 83[[rust-analyzer.checkOnSave.target]]rust-analyzer.checkOnSave.target (default: `null`)::
34 Check for a specific target. Defaults to `#rust-analyzer.cargo.target#`. 84+
85--
86Check for a specific target. Defaults to
87`#rust-analyzer.cargo.target#`.
88--
35[[rust-analyzer.checkOnSave.extraArgs]]rust-analyzer.checkOnSave.extraArgs (default: `[]`):: 89[[rust-analyzer.checkOnSave.extraArgs]]rust-analyzer.checkOnSave.extraArgs (default: `[]`)::
36 Extra arguments for `cargo check`. 90+
91--
92Extra arguments for `cargo check`.
93--
37[[rust-analyzer.checkOnSave.features]]rust-analyzer.checkOnSave.features (default: `null`):: 94[[rust-analyzer.checkOnSave.features]]rust-analyzer.checkOnSave.features (default: `null`)::
38 List of features to activate. Defaults to `#rust-analyzer.cargo.features#`. 95+
96--
97List of features to activate. Defaults to
98`#rust-analyzer.cargo.features#`.
99--
39[[rust-analyzer.checkOnSave.overrideCommand]]rust-analyzer.checkOnSave.overrideCommand (default: `null`):: 100[[rust-analyzer.checkOnSave.overrideCommand]]rust-analyzer.checkOnSave.overrideCommand (default: `null`)::
40 Advanced option, fully override the command rust-analyzer uses for checking. The command should include `--message-format=json` or similar option. 101+
102--
103Advanced option, fully override the command rust-analyzer uses for
104checking. The command should include `--message-format=json` or
105similar option.
106--
41[[rust-analyzer.completion.addCallArgumentSnippets]]rust-analyzer.completion.addCallArgumentSnippets (default: `true`):: 107[[rust-analyzer.completion.addCallArgumentSnippets]]rust-analyzer.completion.addCallArgumentSnippets (default: `true`)::
42 Whether to add argument snippets when completing functions. 108+
109--
110Whether to add argument snippets when completing functions.
111--
43[[rust-analyzer.completion.addCallParenthesis]]rust-analyzer.completion.addCallParenthesis (default: `true`):: 112[[rust-analyzer.completion.addCallParenthesis]]rust-analyzer.completion.addCallParenthesis (default: `true`)::
44 Whether to add parenthesis when completing functions. 113+
114--
115Whether to add parenthesis when completing functions.
116--
45[[rust-analyzer.completion.postfix.enable]]rust-analyzer.completion.postfix.enable (default: `true`):: 117[[rust-analyzer.completion.postfix.enable]]rust-analyzer.completion.postfix.enable (default: `true`)::
46 Whether to show postfix snippets like `dbg`, `if`, `not`, etc. 118+
119--
120Whether to show postfix snippets like `dbg`, `if`, `not`, etc.
121--
47[[rust-analyzer.completion.autoimport.enable]]rust-analyzer.completion.autoimport.enable (default: `true`):: 122[[rust-analyzer.completion.autoimport.enable]]rust-analyzer.completion.autoimport.enable (default: `true`)::
48 Toggles the additional completions that automatically add imports when completed. Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. 123+
124--
125Toggles the additional completions that automatically add imports when completed.
126Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.
127--
49[[rust-analyzer.diagnostics.enable]]rust-analyzer.diagnostics.enable (default: `true`):: 128[[rust-analyzer.diagnostics.enable]]rust-analyzer.diagnostics.enable (default: `true`)::
50 Whether to show native rust-analyzer diagnostics. 129+
130--
131Whether to show native rust-analyzer diagnostics.
132--
51[[rust-analyzer.diagnostics.enableExperimental]]rust-analyzer.diagnostics.enableExperimental (default: `true`):: 133[[rust-analyzer.diagnostics.enableExperimental]]rust-analyzer.diagnostics.enableExperimental (default: `true`)::
52 Whether to show experimental rust-analyzer diagnostics that might have more false positives than usual. 134+
135--
136Whether to show experimental rust-analyzer diagnostics that might
137have more false positives than usual.
138--
53[[rust-analyzer.diagnostics.disabled]]rust-analyzer.diagnostics.disabled (default: `[]`):: 139[[rust-analyzer.diagnostics.disabled]]rust-analyzer.diagnostics.disabled (default: `[]`)::
54 List of rust-analyzer diagnostics to disable. 140+
141--
142List of rust-analyzer diagnostics to disable.
143--
55[[rust-analyzer.diagnostics.warningsAsHint]]rust-analyzer.diagnostics.warningsAsHint (default: `[]`):: 144[[rust-analyzer.diagnostics.warningsAsHint]]rust-analyzer.diagnostics.warningsAsHint (default: `[]`)::
56 List of warnings that should be displayed with info severity.\n\nThe warnings will be indicated by a blue squiggly underline in code and a blue icon in the `Problems Panel`. 145+
146--
147List of warnings that should be displayed with info severity.
148
149The warnings will be indicated by a blue squiggly underline in code
150and a blue icon in the `Problems Panel`.
151--
57[[rust-analyzer.diagnostics.warningsAsInfo]]rust-analyzer.diagnostics.warningsAsInfo (default: `[]`):: 152[[rust-analyzer.diagnostics.warningsAsInfo]]rust-analyzer.diagnostics.warningsAsInfo (default: `[]`)::
58 List of warnings that should be displayed with hint severity.\n\nThe warnings will be indicated by faded text or three dots in code and will not show up in the `Problems Panel`. 153+
154--
155List of warnings that should be displayed with hint severity.
156
157The warnings will be indicated by faded text or three dots in code
158and will not show up in the `Problems Panel`.
159--
59[[rust-analyzer.files.watcher]]rust-analyzer.files.watcher (default: `"client"`):: 160[[rust-analyzer.files.watcher]]rust-analyzer.files.watcher (default: `"client"`)::
60 Controls file watching implementation. 161+
162--
163Controls file watching implementation.
164--
61[[rust-analyzer.files.excludeDirs]]rust-analyzer.files.excludeDirs (default: `[]`):: 165[[rust-analyzer.files.excludeDirs]]rust-analyzer.files.excludeDirs (default: `[]`)::
62 These directories will be ignored by rust-analyzer. 166+
167--
168These directories will be ignored by rust-analyzer.
169--
63[[rust-analyzer.hoverActions.debug]]rust-analyzer.hoverActions.debug (default: `true`):: 170[[rust-analyzer.hoverActions.debug]]rust-analyzer.hoverActions.debug (default: `true`)::
64 Whether to show `Debug` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set. 171+
172--
173Whether to show `Debug` action. Only applies when
174`#rust-analyzer.hoverActions.enable#` is set.
175--
65[[rust-analyzer.hoverActions.enable]]rust-analyzer.hoverActions.enable (default: `true`):: 176[[rust-analyzer.hoverActions.enable]]rust-analyzer.hoverActions.enable (default: `true`)::
66 Whether to show HoverActions in Rust files. 177+
178--
179Whether to show HoverActions in Rust files.
180--
67[[rust-analyzer.hoverActions.gotoTypeDef]]rust-analyzer.hoverActions.gotoTypeDef (default: `true`):: 181[[rust-analyzer.hoverActions.gotoTypeDef]]rust-analyzer.hoverActions.gotoTypeDef (default: `true`)::
68 Whether to show `Go to Type Definition` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set. 182+
183--
184Whether to show `Go to Type Definition` action. Only applies when
185`#rust-analyzer.hoverActions.enable#` is set.
186--
69[[rust-analyzer.hoverActions.implementations]]rust-analyzer.hoverActions.implementations (default: `true`):: 187[[rust-analyzer.hoverActions.implementations]]rust-analyzer.hoverActions.implementations (default: `true`)::
70 Whether to show `Implementations` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set. 188+
189--
190Whether to show `Implementations` action. Only applies when
191`#rust-analyzer.hoverActions.enable#` is set.
192--
71[[rust-analyzer.hoverActions.run]]rust-analyzer.hoverActions.run (default: `true`):: 193[[rust-analyzer.hoverActions.run]]rust-analyzer.hoverActions.run (default: `true`)::
72 Whether to show `Run` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set. 194+
195--
196Whether to show `Run` action. Only applies when
197`#rust-analyzer.hoverActions.enable#` is set.
198--
73[[rust-analyzer.hoverActions.linksInHover]]rust-analyzer.hoverActions.linksInHover (default: `true`):: 199[[rust-analyzer.hoverActions.linksInHover]]rust-analyzer.hoverActions.linksInHover (default: `true`)::
74 Use markdown syntax for links in hover. 200+
201--
202Use markdown syntax for links in hover.
203--
75[[rust-analyzer.inlayHints.chainingHints]]rust-analyzer.inlayHints.chainingHints (default: `true`):: 204[[rust-analyzer.inlayHints.chainingHints]]rust-analyzer.inlayHints.chainingHints (default: `true`)::
76 Whether to show inlay type hints for method chains. 205+
206--
207Whether to show inlay type hints for method chains.
208--
77[[rust-analyzer.inlayHints.maxLength]]rust-analyzer.inlayHints.maxLength (default: `null`):: 209[[rust-analyzer.inlayHints.maxLength]]rust-analyzer.inlayHints.maxLength (default: `null`)::
78 Maximum length for inlay hints. Default is unlimited. 210+
211--
212Maximum length for inlay hints. Default is unlimited.
213--
79[[rust-analyzer.inlayHints.parameterHints]]rust-analyzer.inlayHints.parameterHints (default: `true`):: 214[[rust-analyzer.inlayHints.parameterHints]]rust-analyzer.inlayHints.parameterHints (default: `true`)::
80 Whether to show function parameter name inlay hints at the call site. 215+
216--
217Whether to show function parameter name inlay hints at the call
218site.
219--
81[[rust-analyzer.inlayHints.typeHints]]rust-analyzer.inlayHints.typeHints (default: `true`):: 220[[rust-analyzer.inlayHints.typeHints]]rust-analyzer.inlayHints.typeHints (default: `true`)::
82 Whether to show inlay type hints for variables. 221+
222--
223Whether to show inlay type hints for variables.
224--
83[[rust-analyzer.lens.debug]]rust-analyzer.lens.debug (default: `true`):: 225[[rust-analyzer.lens.debug]]rust-analyzer.lens.debug (default: `true`)::
84 Whether to show `Debug` lens. Only applies when `#rust-analyzer.lens.enable#` is set. 226+
227--
228Whether to show `Debug` lens. Only applies when
229`#rust-analyzer.lens.enable#` is set.
230--
85[[rust-analyzer.lens.enable]]rust-analyzer.lens.enable (default: `true`):: 231[[rust-analyzer.lens.enable]]rust-analyzer.lens.enable (default: `true`)::
86 Whether to show CodeLens in Rust files. 232+
233--
234Whether to show CodeLens in Rust files.
235--
87[[rust-analyzer.lens.implementations]]rust-analyzer.lens.implementations (default: `true`):: 236[[rust-analyzer.lens.implementations]]rust-analyzer.lens.implementations (default: `true`)::
88 Whether to show `Implementations` lens. Only applies when `#rust-analyzer.lens.enable#` is set. 237+
238--
239Whether to show `Implementations` lens. Only applies when
240`#rust-analyzer.lens.enable#` is set.
241--
89[[rust-analyzer.lens.run]]rust-analyzer.lens.run (default: `true`):: 242[[rust-analyzer.lens.run]]rust-analyzer.lens.run (default: `true`)::
90 Whether to show `Run` lens. Only applies when `#rust-analyzer.lens.enable#` is set. 243+
244--
245Whether to show `Run` lens. Only applies when
246`#rust-analyzer.lens.enable#` is set.
247--
91[[rust-analyzer.lens.methodReferences]]rust-analyzer.lens.methodReferences (default: `false`):: 248[[rust-analyzer.lens.methodReferences]]rust-analyzer.lens.methodReferences (default: `false`)::
92 Whether to show `Method References` lens. Only applies when `#rust-analyzer.lens.enable#` is set. 249+
250--
251Whether to show `Method References` lens. Only applies when
252`#rust-analyzer.lens.enable#` is set.
253--
93[[rust-analyzer.lens.references]]rust-analyzer.lens.references (default: `false`):: 254[[rust-analyzer.lens.references]]rust-analyzer.lens.references (default: `false`)::
94 Whether to show `References` lens. Only applies when `#rust-analyzer.lens.enable#` is set. 255+
256--
257Whether to show `References` lens. Only applies when
258`#rust-analyzer.lens.enable#` is set.
259--
95[[rust-analyzer.linkedProjects]]rust-analyzer.linkedProjects (default: `[]`):: 260[[rust-analyzer.linkedProjects]]rust-analyzer.linkedProjects (default: `[]`)::
96 Disable project auto-discovery in favor of explicitly specified set of projects.\n\nElements must be paths pointing to `Cargo.toml`, `rust-project.json`, or JSON objects in `rust-project.json` format. 261+
262--
263Disable project auto-discovery in favor of explicitly specified set
264of projects.
265
266Elements must be paths pointing to `Cargo.toml`,
267`rust-project.json`, or JSON objects in `rust-project.json` format.
268--
97[[rust-analyzer.lruCapacity]]rust-analyzer.lruCapacity (default: `null`):: 269[[rust-analyzer.lruCapacity]]rust-analyzer.lruCapacity (default: `null`)::
98 Number of syntax trees rust-analyzer keeps in memory. Defaults to 128. 270+
271--
272Number of syntax trees rust-analyzer keeps in memory. Defaults to 128.
273--
99[[rust-analyzer.notifications.cargoTomlNotFound]]rust-analyzer.notifications.cargoTomlNotFound (default: `true`):: 274[[rust-analyzer.notifications.cargoTomlNotFound]]rust-analyzer.notifications.cargoTomlNotFound (default: `true`)::
100 Whether to show `can't find Cargo.toml` error message. 275+
276--
277Whether to show `can't find Cargo.toml` error message.
278--
101[[rust-analyzer.procMacro.enable]]rust-analyzer.procMacro.enable (default: `false`):: 279[[rust-analyzer.procMacro.enable]]rust-analyzer.procMacro.enable (default: `false`)::
102 Enable support for procedural macros, implies `#rust-analyzer.cargo.runBuildScripts#`. 280+
281--
282Enable support for procedural macros, implies `#rust-analyzer.cargo.runBuildScripts#`.
283--
103[[rust-analyzer.procMacro.server]]rust-analyzer.procMacro.server (default: `null`):: 284[[rust-analyzer.procMacro.server]]rust-analyzer.procMacro.server (default: `null`)::
104 Internal config, path to proc-macro server executable (typically, this is rust-analyzer itself, but we override this in tests). 285+
286--
287Internal config, path to proc-macro server executable (typically,
288this is rust-analyzer itself, but we override this in tests).
289--
105[[rust-analyzer.runnables.overrideCargo]]rust-analyzer.runnables.overrideCargo (default: `null`):: 290[[rust-analyzer.runnables.overrideCargo]]rust-analyzer.runnables.overrideCargo (default: `null`)::
106 Command to be executed instead of 'cargo' for runnables. 291+
292--
293Command to be executed instead of 'cargo' for runnables.
294--
107[[rust-analyzer.runnables.cargoExtraArgs]]rust-analyzer.runnables.cargoExtraArgs (default: `[]`):: 295[[rust-analyzer.runnables.cargoExtraArgs]]rust-analyzer.runnables.cargoExtraArgs (default: `[]`)::
108 Additional arguments to be passed to cargo for runnables such as tests or binaries.\nFor example, it may be `--release`. 296+
297--
298Additional arguments to be passed to cargo for runnables such as
299tests or binaries. For example, it may be `--release`.
300--
109[[rust-analyzer.rustcSource]]rust-analyzer.rustcSource (default: `null`):: 301[[rust-analyzer.rustcSource]]rust-analyzer.rustcSource (default: `null`)::
110 Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private projects, or "discover" to try to automatically find it. Any project which uses rust-analyzer with the rustcPrivate crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it. This option is not reloaded automatically; you must restart rust-analyzer for it to take effect. 302+
303--
304Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private
305projects, or "discover" to try to automatically find it.
306
307Any project which uses rust-analyzer with the rustcPrivate
308crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it.
309
310This option is not reloaded automatically; you must restart rust-analyzer for it to take effect.
311--
111[[rust-analyzer.rustfmt.extraArgs]]rust-analyzer.rustfmt.extraArgs (default: `[]`):: 312[[rust-analyzer.rustfmt.extraArgs]]rust-analyzer.rustfmt.extraArgs (default: `[]`)::
112 Additional arguments to `rustfmt`. 313+
314--
315Additional arguments to `rustfmt`.
316--
113[[rust-analyzer.rustfmt.overrideCommand]]rust-analyzer.rustfmt.overrideCommand (default: `null`):: 317[[rust-analyzer.rustfmt.overrideCommand]]rust-analyzer.rustfmt.overrideCommand (default: `null`)::
114 Advanced option, fully override the command rust-analyzer uses for formatting. 318+
319--
320Advanced option, fully override the command rust-analyzer uses for
321formatting.
322--
diff --git a/editors/code/package.json b/editors/code/package.json
index e15e7875b..b29f006f0 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -397,7 +397,7 @@
397 "type": "boolean" 397 "type": "boolean"
398 }, 398 },
399 "rust-analyzer.cargo.autoreload": { 399 "rust-analyzer.cargo.autoreload": {
400 "markdownDescription": "Automatically refresh project info via `cargo metadata` on `Cargo.toml` changes.", 400 "markdownDescription": "Automatically refresh project info via `cargo metadata` on\n`Cargo.toml` changes.",
401 "default": true, 401 "default": true,
402 "type": "boolean" 402 "type": "boolean"
403 }, 403 },
@@ -443,7 +443,7 @@
443 "type": "boolean" 443 "type": "boolean"
444 }, 444 },
445 "rust-analyzer.checkOnSave.allFeatures": { 445 "rust-analyzer.checkOnSave.allFeatures": {
446 "markdownDescription": "Check with all features (`--all-features`). Defaults to `#rust-analyzer.cargo.allFeatures#`.", 446 "markdownDescription": "Check with all features (`--all-features`).\nDefaults to `#rust-analyzer.cargo.allFeatures#`.",
447 "default": null, 447 "default": null,
448 "type": [ 448 "type": [
449 "null", 449 "null",
@@ -469,7 +469,7 @@
469 ] 469 ]
470 }, 470 },
471 "rust-analyzer.checkOnSave.target": { 471 "rust-analyzer.checkOnSave.target": {
472 "markdownDescription": "Check for a specific target. Defaults to `#rust-analyzer.cargo.target#`.", 472 "markdownDescription": "Check for a specific target. Defaults to\n`#rust-analyzer.cargo.target#`.",
473 "default": null, 473 "default": null,
474 "type": [ 474 "type": [
475 "null", 475 "null",
@@ -485,7 +485,7 @@
485 } 485 }
486 }, 486 },
487 "rust-analyzer.checkOnSave.features": { 487 "rust-analyzer.checkOnSave.features": {
488 "markdownDescription": "List of features to activate. Defaults to `#rust-analyzer.cargo.features#`.", 488 "markdownDescription": "List of features to activate. Defaults to\n`#rust-analyzer.cargo.features#`.",
489 "default": null, 489 "default": null,
490 "type": [ 490 "type": [
491 "null", 491 "null",
@@ -496,7 +496,7 @@
496 } 496 }
497 }, 497 },
498 "rust-analyzer.checkOnSave.overrideCommand": { 498 "rust-analyzer.checkOnSave.overrideCommand": {
499 "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for checking. The command should include `--message-format=json` or similar option.", 499 "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for\nchecking. The command should include `--message-format=json` or\nsimilar option.",
500 "default": null, 500 "default": null,
501 "type": [ 501 "type": [
502 "null", 502 "null",
@@ -522,7 +522,7 @@
522 "type": "boolean" 522 "type": "boolean"
523 }, 523 },
524 "rust-analyzer.completion.autoimport.enable": { 524 "rust-analyzer.completion.autoimport.enable": {
525 "markdownDescription": "Toggles the additional completions that automatically add imports when completed. Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.", 525 "markdownDescription": "Toggles the additional completions that automatically add imports when completed.\nNote that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.",
526 "default": true, 526 "default": true,
527 "type": "boolean" 527 "type": "boolean"
528 }, 528 },
@@ -532,7 +532,7 @@
532 "type": "boolean" 532 "type": "boolean"
533 }, 533 },
534 "rust-analyzer.diagnostics.enableExperimental": { 534 "rust-analyzer.diagnostics.enableExperimental": {
535 "markdownDescription": "Whether to show experimental rust-analyzer diagnostics that might have more false positives than usual.", 535 "markdownDescription": "Whether to show experimental rust-analyzer diagnostics that might\nhave more false positives than usual.",
536 "default": true, 536 "default": true,
537 "type": "boolean" 537 "type": "boolean"
538 }, 538 },
@@ -546,7 +546,7 @@
546 "uniqueItems": true 546 "uniqueItems": true
547 }, 547 },
548 "rust-analyzer.diagnostics.warningsAsHint": { 548 "rust-analyzer.diagnostics.warningsAsHint": {
549 "markdownDescription": "List of warnings that should be displayed with info severity.\\n\\nThe warnings will be indicated by a blue squiggly underline in code and a blue icon in the `Problems Panel`.", 549 "markdownDescription": "List of warnings that should be displayed with info severity.\n\nThe warnings will be indicated by a blue squiggly underline in code\nand a blue icon in the `Problems Panel`.",
550 "default": [], 550 "default": [],
551 "type": "array", 551 "type": "array",
552 "items": { 552 "items": {
@@ -554,7 +554,7 @@
554 } 554 }
555 }, 555 },
556 "rust-analyzer.diagnostics.warningsAsInfo": { 556 "rust-analyzer.diagnostics.warningsAsInfo": {
557 "markdownDescription": "List of warnings that should be displayed with hint severity.\\n\\nThe warnings will be indicated by faded text or three dots in code and will not show up in the `Problems Panel`.", 557 "markdownDescription": "List of warnings that should be displayed with hint severity.\n\nThe warnings will be indicated by faded text or three dots in code\nand will not show up in the `Problems Panel`.",
558 "default": [], 558 "default": [],
559 "type": "array", 559 "type": "array",
560 "items": { 560 "items": {
@@ -575,7 +575,7 @@
575 } 575 }
576 }, 576 },
577 "rust-analyzer.hoverActions.debug": { 577 "rust-analyzer.hoverActions.debug": {
578 "markdownDescription": "Whether to show `Debug` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set.", 578 "markdownDescription": "Whether to show `Debug` action. Only applies when\n`#rust-analyzer.hoverActions.enable#` is set.",
579 "default": true, 579 "default": true,
580 "type": "boolean" 580 "type": "boolean"
581 }, 581 },
@@ -585,17 +585,17 @@
585 "type": "boolean" 585 "type": "boolean"
586 }, 586 },
587 "rust-analyzer.hoverActions.gotoTypeDef": { 587 "rust-analyzer.hoverActions.gotoTypeDef": {
588 "markdownDescription": "Whether to show `Go to Type Definition` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set.", 588 "markdownDescription": "Whether to show `Go to Type Definition` action. Only applies when\n`#rust-analyzer.hoverActions.enable#` is set.",
589 "default": true, 589 "default": true,
590 "type": "boolean" 590 "type": "boolean"
591 }, 591 },
592 "rust-analyzer.hoverActions.implementations": { 592 "rust-analyzer.hoverActions.implementations": {
593 "markdownDescription": "Whether to show `Implementations` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set.", 593 "markdownDescription": "Whether to show `Implementations` action. Only applies when\n`#rust-analyzer.hoverActions.enable#` is set.",
594 "default": true, 594 "default": true,
595 "type": "boolean" 595 "type": "boolean"
596 }, 596 },
597 "rust-analyzer.hoverActions.run": { 597 "rust-analyzer.hoverActions.run": {
598 "markdownDescription": "Whether to show `Run` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set.", 598 "markdownDescription": "Whether to show `Run` action. Only applies when\n`#rust-analyzer.hoverActions.enable#` is set.",
599 "default": true, 599 "default": true,
600 "type": "boolean" 600 "type": "boolean"
601 }, 601 },
@@ -619,7 +619,7 @@
619 "minimum": 0 619 "minimum": 0
620 }, 620 },
621 "rust-analyzer.inlayHints.parameterHints": { 621 "rust-analyzer.inlayHints.parameterHints": {
622 "markdownDescription": "Whether to show function parameter name inlay hints at the call site.", 622 "markdownDescription": "Whether to show function parameter name inlay hints at the call\nsite.",
623 "default": true, 623 "default": true,
624 "type": "boolean" 624 "type": "boolean"
625 }, 625 },
@@ -629,7 +629,7 @@
629 "type": "boolean" 629 "type": "boolean"
630 }, 630 },
631 "rust-analyzer.lens.debug": { 631 "rust-analyzer.lens.debug": {
632 "markdownDescription": "Whether to show `Debug` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", 632 "markdownDescription": "Whether to show `Debug` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.",
633 "default": true, 633 "default": true,
634 "type": "boolean" 634 "type": "boolean"
635 }, 635 },
@@ -639,27 +639,27 @@
639 "type": "boolean" 639 "type": "boolean"
640 }, 640 },
641 "rust-analyzer.lens.implementations": { 641 "rust-analyzer.lens.implementations": {
642 "markdownDescription": "Whether to show `Implementations` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", 642 "markdownDescription": "Whether to show `Implementations` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.",
643 "default": true, 643 "default": true,
644 "type": "boolean" 644 "type": "boolean"
645 }, 645 },
646 "rust-analyzer.lens.run": { 646 "rust-analyzer.lens.run": {
647 "markdownDescription": "Whether to show `Run` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", 647 "markdownDescription": "Whether to show `Run` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.",
648 "default": true, 648 "default": true,
649 "type": "boolean" 649 "type": "boolean"
650 }, 650 },
651 "rust-analyzer.lens.methodReferences": { 651 "rust-analyzer.lens.methodReferences": {
652 "markdownDescription": "Whether to show `Method References` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", 652 "markdownDescription": "Whether to show `Method References` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.",
653 "default": false, 653 "default": false,
654 "type": "boolean" 654 "type": "boolean"
655 }, 655 },
656 "rust-analyzer.lens.references": { 656 "rust-analyzer.lens.references": {
657 "markdownDescription": "Whether to show `References` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", 657 "markdownDescription": "Whether to show `References` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.",
658 "default": false, 658 "default": false,
659 "type": "boolean" 659 "type": "boolean"
660 }, 660 },
661 "rust-analyzer.linkedProjects": { 661 "rust-analyzer.linkedProjects": {
662 "markdownDescription": "Disable project auto-discovery in favor of explicitly specified set of projects.\\n\\nElements must be paths pointing to `Cargo.toml`, `rust-project.json`, or JSON objects in `rust-project.json` format.", 662 "markdownDescription": "Disable project auto-discovery in favor of explicitly specified set\nof projects.\n\nElements must be paths pointing to `Cargo.toml`,\n`rust-project.json`, or JSON objects in `rust-project.json` format.",
663 "default": [], 663 "default": [],
664 "type": "array", 664 "type": "array",
665 "items": { 665 "items": {
@@ -689,7 +689,7 @@
689 "type": "boolean" 689 "type": "boolean"
690 }, 690 },
691 "rust-analyzer.procMacro.server": { 691 "rust-analyzer.procMacro.server": {
692 "markdownDescription": "Internal config, path to proc-macro server executable (typically, this is rust-analyzer itself, but we override this in tests).", 692 "markdownDescription": "Internal config, path to proc-macro server executable (typically,\nthis is rust-analyzer itself, but we override this in tests).",
693 "default": null, 693 "default": null,
694 "type": [ 694 "type": [
695 "null", 695 "null",
@@ -705,7 +705,7 @@
705 ] 705 ]
706 }, 706 },
707 "rust-analyzer.runnables.cargoExtraArgs": { 707 "rust-analyzer.runnables.cargoExtraArgs": {
708 "markdownDescription": "Additional arguments to be passed to cargo for runnables such as tests or binaries.\\nFor example, it may be `--release`.", 708 "markdownDescription": "Additional arguments to be passed to cargo for runnables such as\ntests or binaries. For example, it may be `--release`.",
709 "default": [], 709 "default": [],
710 "type": "array", 710 "type": "array",
711 "items": { 711 "items": {
@@ -713,7 +713,7 @@
713 } 713 }
714 }, 714 },
715 "rust-analyzer.rustcSource": { 715 "rust-analyzer.rustcSource": {
716 "markdownDescription": "Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private projects, or \"discover\" to try to automatically find it. Any project which uses rust-analyzer with the rustcPrivate crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it. This option is not reloaded automatically; you must restart rust-analyzer for it to take effect.", 716 "markdownDescription": "Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private\nprojects, or \"discover\" to try to automatically find it.\n\nAny project which uses rust-analyzer with the rustcPrivate\ncrates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it.\n\nThis option is not reloaded automatically; you must restart rust-analyzer for it to take effect.",
717 "default": null, 717 "default": null,
718 "type": [ 718 "type": [
719 "null", 719 "null",
@@ -729,7 +729,7 @@
729 } 729 }
730 }, 730 },
731 "rust-analyzer.rustfmt.overrideCommand": { 731 "rust-analyzer.rustfmt.overrideCommand": {
732 "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for formatting.", 732 "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for\nformatting.",
733 "default": null, 733 "default": null,
734 "type": [ 734 "type": [
735 "null", 735 "null",