diff options
author | Kirill Bulatov <[email protected]> | 2021-03-08 12:09:20 +0000 |
---|---|---|
committer | Kirill Bulatov <[email protected]> | 2021-03-08 21:59:37 +0000 |
commit | 5168ab16e14679e16a472c0ab13b1bbc32dc95f3 (patch) | |
tree | 79660cf0166a55c5abd97d95284224cc3772cf0c | |
parent | dccbb38d2e28bfeb53f31c13de3b83e72f1a476c (diff) |
Add rustdocs and use better names
-rw-r--r-- | crates/ide_assists/src/handlers/auto_import.rs | 8 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/qualify_path.rs | 14 | ||||
-rw-r--r-- | crates/ide_db/src/helpers/import_assets.rs | 82 |
3 files changed, 64 insertions, 40 deletions
diff --git a/crates/ide_assists/src/handlers/auto_import.rs b/crates/ide_assists/src/handlers/auto_import.rs index 5546c3a4e..7caee8df0 100644 --- a/crates/ide_assists/src/handlers/auto_import.rs +++ b/crates/ide_assists/src/handlers/auto_import.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | use ide_db::helpers::{ | 1 | use 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 | }; |
6 | use syntax::{ast, AstNode, SyntaxNode}; | 6 | use syntax::{ast, AstNode, SyntaxNode}; |
7 | 7 | ||
@@ -93,7 +93,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
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 import.original_item_name(ctx.db()) { | 96 | let name = match item_name(ctx.db(), import.original_item) { |
97 | Some(name) => name, | 97 | Some(name) => name, |
98 | None => continue, | 98 | None => continue, |
99 | }; | 99 | }; |
@@ -130,10 +130,10 @@ fn import_group_message(import_candidate: &ImportCandidate) -> GroupLabel { | |||
130 | let name = match import_candidate { | 130 | let name = match import_candidate { |
131 | ImportCandidate::Path(candidate) => format!("Import {}", candidate.name.text()), | 131 | ImportCandidate::Path(candidate) => format!("Import {}", candidate.name.text()), |
132 | ImportCandidate::TraitAssocItem(candidate) => { | 132 | ImportCandidate::TraitAssocItem(candidate) => { |
133 | format!("Import a trait for item {}", candidate.name.text()) | 133 | format!("Import a trait for item {}", candidate.assoc_item_name.text()) |
134 | } | 134 | } |
135 | ImportCandidate::TraitMethod(candidate) => { | 135 | ImportCandidate::TraitMethod(candidate) => { |
136 | format!("Import a trait for method {}", candidate.name.text()) | 136 | format!("Import a trait for method {}", candidate.assoc_item_name.text()) |
137 | } | 137 | } |
138 | }; | 138 | }; |
139 | GroupLabel(name) | 139 | GroupLabel(name) |
diff --git a/crates/ide_assists/src/handlers/qualify_path.rs b/crates/ide_assists/src/handlers/qualify_path.rs index b36dd3823..272874ae3 100644 --- a/crates/ide_assists/src/handlers/qualify_path.rs +++ b/crates/ide_assists/src/handlers/qualify_path.rs | |||
@@ -2,8 +2,8 @@ use std::iter; | |||
2 | 2 | ||
3 | use hir::AsAssocItem; | 3 | use hir::AsAssocItem; |
4 | use ide_db::helpers::{ | 4 | use ide_db::helpers::{ |
5 | import_assets::{ImportCandidate, LocatedImport, Qualifier}, | 5 | import_assets::{ImportCandidate, LocatedImport}, |
6 | mod_path_to_ast, | 6 | item_name, mod_path_to_ast, |
7 | }; | 7 | }; |
8 | use ide_db::RootDatabase; | 8 | use ide_db::RootDatabase; |
9 | use syntax::{ | 9 | use syntax::{ |
@@ -48,7 +48,7 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
48 | 48 | ||
49 | let qualify_candidate = match candidate { | 49 | let qualify_candidate = match candidate { |
50 | ImportCandidate::Path(candidate) => { | 50 | ImportCandidate::Path(candidate) => { |
51 | if !matches!(candidate.qualifier, Qualifier::Absent) { | 51 | if candidate.qualifier.is_some() { |
52 | cov_mark::hit!(qualify_path_qualifier_start); | 52 | cov_mark::hit!(qualify_path_qualifier_start); |
53 | let path = ast::Path::cast(syntax_under_caret)?; | 53 | let path = ast::Path::cast(syntax_under_caret)?; |
54 | let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?); | 54 | let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?); |
@@ -191,20 +191,22 @@ fn item_as_trait(db: &RootDatabase, item: hir::ItemInNs) -> Option<hir::Trait> { | |||
191 | fn group_label(candidate: &ImportCandidate) -> GroupLabel { | 191 | fn group_label(candidate: &ImportCandidate) -> GroupLabel { |
192 | let name = match candidate { | 192 | let name = match candidate { |
193 | ImportCandidate::Path(it) => &it.name, | 193 | ImportCandidate::Path(it) => &it.name, |
194 | ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => &it.name, | 194 | ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => { |
195 | &it.assoc_item_name | ||
196 | } | ||
195 | } | 197 | } |
196 | .text(); | 198 | .text(); |
197 | GroupLabel(format!("Qualify {}", name)) | 199 | GroupLabel(format!("Qualify {}", name)) |
198 | } | 200 | } |
199 | 201 | ||
200 | fn label(db: &RootDatabase, candidate: &ImportCandidate, import: &LocatedImport) -> String { | 202 | fn label(db: &RootDatabase, candidate: &ImportCandidate, import: &LocatedImport) -> String { |
201 | let display_path = match import.original_item_name(db) { | 203 | let display_path = match item_name(db, import.original_item) { |
202 | Some(display_path) => display_path.to_string(), | 204 | Some(display_path) => display_path.to_string(), |
203 | None => "{unknown}".to_string(), | 205 | None => "{unknown}".to_string(), |
204 | }; | 206 | }; |
205 | match candidate { | 207 | match candidate { |
206 | ImportCandidate::Path(candidate) => { | 208 | ImportCandidate::Path(candidate) => { |
207 | if !matches!(candidate.qualifier, Qualifier::Absent) { | 209 | if candidate.qualifier.is_some() { |
208 | format!("Qualify with `{}`", display_path) | 210 | format!("Qualify with `{}`", display_path) |
209 | } else { | 211 | } else { |
210 | format!("Qualify as `{}`", display_path) | 212 | format!("Qualify as `{}`", display_path) |
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs index f2866af13..3d9df463d 100644 --- a/crates/ide_db/src/helpers/import_assets.rs +++ b/crates/ide_db/src/helpers/import_assets.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! Look up accessible paths for items. | 1 | //! Look up accessible paths for items. |
2 | use hir::{ | 2 | use hir::{ |
3 | AsAssocItem, AssocItem, AssocItemContainer, Crate, ItemInNs, MacroDef, ModPath, Module, | 3 | AsAssocItem, AssocItem, AssocItemContainer, Crate, ItemInNs, MacroDef, ModPath, Module, |
4 | ModuleDef, Name, PathResolution, PrefixKind, ScopeDef, Semantics, Type, | 4 | ModuleDef, PathResolution, PrefixKind, ScopeDef, Semantics, Type, |
5 | }; | 5 | }; |
6 | use itertools::Itertools; | 6 | use itertools::Itertools; |
7 | use rustc_hash::FxHashSet; | 7 | use rustc_hash::FxHashSet; |
@@ -14,11 +14,16 @@ use crate::{ | |||
14 | 14 | ||
15 | use super::item_name; | 15 | use super::item_name; |
16 | 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. | ||
17 | #[derive(Debug)] | 22 | #[derive(Debug)] |
18 | pub enum ImportCandidate { | 23 | pub enum ImportCandidate { |
19 | // A path, qualified (`std::collections::HashMap`) or not (`HashMap`). | 24 | /// A path, qualified (`std::collections::HashMap`) or not (`HashMap`). |
20 | Path(PathImportCandidate), | 25 | Path(PathImportCandidate), |
21 | /// A trait associated function (with no self parameter) or associated constant. | 26 | /// A trait associated function (with no self parameter) or an associated constant. |
22 | /// 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 |
23 | /// and `name` is the `test_function` | 28 | /// and `name` is the `test_function` |
24 | TraitAssocItem(TraitImportCandidate), | 29 | TraitAssocItem(TraitImportCandidate), |
@@ -28,27 +33,40 @@ pub enum ImportCandidate { | |||
28 | TraitMethod(TraitImportCandidate), | 33 | TraitMethod(TraitImportCandidate), |
29 | } | 34 | } |
30 | 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. | ||
31 | #[derive(Debug)] | 39 | #[derive(Debug)] |
32 | pub struct TraitImportCandidate { | 40 | pub struct TraitImportCandidate { |
41 | /// A type of the item that has the associated item accessed at. | ||
33 | pub receiver_ty: Type, | 42 | pub receiver_ty: Type, |
34 | pub name: NameToImport, | 43 | /// The associated item name that the trait to import should contain. |
44 | pub assoc_item_name: NameToImport, | ||
35 | } | 45 | } |
36 | 46 | ||
47 | /// Path import for a given name, qualified or not. | ||
37 | #[derive(Debug)] | 48 | #[derive(Debug)] |
38 | pub struct PathImportCandidate { | 49 | pub struct PathImportCandidate { |
39 | pub qualifier: Qualifier, | 50 | /// Optional qualifier before name. |
51 | pub qualifier: Option<FirstSegmentUnresolved>, | ||
52 | /// The name the item (struct, trait, enum, etc.) should have. | ||
40 | pub name: NameToImport, | 53 | pub name: NameToImport, |
41 | } | 54 | } |
42 | 55 | ||
56 | /// A qualifier that has a first segment and it's unresolved. | ||
43 | #[derive(Debug)] | 57 | #[derive(Debug)] |
44 | pub enum Qualifier { | 58 | pub struct FirstSegmentUnresolved { |
45 | Absent, | 59 | fist_segment: ast::NameRef, |
46 | FirstSegmentUnresolved(ast::NameRef, ModPath), | 60 | full_qualifier: ModPath, |
47 | } | 61 | } |
48 | 62 | ||
63 | /// A name that will be used during item lookups. | ||
49 | #[derive(Debug)] | 64 | #[derive(Debug)] |
50 | pub enum NameToImport { | 65 | pub enum NameToImport { |
66 | /// Requires items with names that exactly match the given string, case-sensitive. | ||
51 | 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. | ||
52 | Fuzzy(String), | 70 | Fuzzy(String), |
53 | } | 71 | } |
54 | 72 | ||
@@ -61,6 +79,7 @@ impl NameToImport { | |||
61 | } | 79 | } |
62 | } | 80 | } |
63 | 81 | ||
82 | /// A struct to find imports in the project, given a certain name (or its part) and the context. | ||
64 | #[derive(Debug)] | 83 | #[derive(Debug)] |
65 | pub struct ImportAssets { | 84 | pub struct ImportAssets { |
66 | import_candidate: ImportCandidate, | 85 | import_candidate: ImportCandidate, |
@@ -119,7 +138,7 @@ impl ImportAssets { | |||
119 | Some(Self { | 138 | Some(Self { |
120 | import_candidate: ImportCandidate::TraitMethod(TraitImportCandidate { | 139 | import_candidate: ImportCandidate::TraitMethod(TraitImportCandidate { |
121 | receiver_ty, | 140 | receiver_ty, |
122 | name: NameToImport::Fuzzy(fuzzy_method_name), | 141 | assoc_item_name: NameToImport::Fuzzy(fuzzy_method_name), |
123 | }), | 142 | }), |
124 | module_with_candidate: module_with_method_call, | 143 | module_with_candidate: module_with_method_call, |
125 | candidate_node, | 144 | candidate_node, |
@@ -127,11 +146,22 @@ impl ImportAssets { | |||
127 | } | 146 | } |
128 | } | 147 | } |
129 | 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) | ||
130 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 151 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
131 | pub struct LocatedImport { | 152 | pub struct LocatedImport { |
153 | /// The path to use in the `use` statement for a given candidate to be imported. | ||
132 | pub import_path: ModPath, | 154 | pub import_path: ModPath, |
155 | /// An item that will be imported with the import path given. | ||
133 | pub item_to_import: ItemInNs, | 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. | ||
134 | pub original_item: ItemInNs, | 163 | pub original_item: ItemInNs, |
164 | /// A path of the original item. | ||
135 | pub original_path: Option<ModPath>, | 165 | pub original_path: Option<ModPath>, |
136 | } | 166 | } |
137 | 167 | ||
@@ -144,15 +174,6 @@ impl LocatedImport { | |||
144 | ) -> Self { | 174 | ) -> Self { |
145 | Self { import_path, item_to_import, original_item, original_path } | 175 | Self { import_path, item_to_import, original_item, original_path } |
146 | } | 176 | } |
147 | |||
148 | pub fn original_item_name(&self, db: &RootDatabase) -> Option<Name> { | ||
149 | match self.original_item { | ||
150 | ItemInNs::Types(module_def_id) | ItemInNs::Values(module_def_id) => { | ||
151 | ModuleDef::from(module_def_id).name(db) | ||
152 | } | ||
153 | ItemInNs::Macros(macro_def_id) => MacroDef::from(macro_def_id).name(db), | ||
154 | } | ||
155 | } | ||
156 | } | 177 | } |
157 | 178 | ||
158 | impl ImportAssets { | 179 | impl ImportAssets { |
@@ -229,7 +250,7 @@ impl ImportAssets { | |||
229 | match &self.import_candidate { | 250 | match &self.import_candidate { |
230 | ImportCandidate::Path(candidate) => &candidate.name, | 251 | ImportCandidate::Path(candidate) => &candidate.name, |
231 | ImportCandidate::TraitAssocItem(candidate) | 252 | ImportCandidate::TraitAssocItem(candidate) |
232 | | ImportCandidate::TraitMethod(candidate) => &candidate.name, | 253 | | ImportCandidate::TraitMethod(candidate) => &candidate.assoc_item_name, |
233 | } | 254 | } |
234 | } | 255 | } |
235 | 256 | ||
@@ -279,7 +300,7 @@ fn path_applicable_imports( | |||
279 | let _p = profile::span("import_assets::path_applicable_imports"); | 300 | let _p = profile::span("import_assets::path_applicable_imports"); |
280 | 301 | ||
281 | let (unresolved_first_segment, unresolved_qualifier) = match &path_candidate.qualifier { | 302 | let (unresolved_first_segment, unresolved_qualifier) = match &path_candidate.qualifier { |
282 | Qualifier::Absent => { | 303 | None => { |
283 | return items_with_candidate_name | 304 | return items_with_candidate_name |
284 | .into_iter() | 305 | .into_iter() |
285 | .filter_map(|item| { | 306 | .filter_map(|item| { |
@@ -287,9 +308,10 @@ fn path_applicable_imports( | |||
287 | }) | 308 | }) |
288 | .collect(); | 309 | .collect(); |
289 | } | 310 | } |
290 | Qualifier::FirstSegmentUnresolved(first_segment, qualifier) => { | 311 | Some(first_segment_unresolved) => ( |
291 | (first_segment.to_string(), qualifier.to_string()) | 312 | first_segment_unresolved.fist_segment.to_string(), |
292 | } | 313 | first_segment_unresolved.full_qualifier.to_string(), |
314 | ), | ||
293 | }; | 315 | }; |
294 | 316 | ||
295 | items_with_candidate_name | 317 | items_with_candidate_name |
@@ -516,7 +538,7 @@ impl ImportCandidate { | |||
516 | Some(_) => None, | 538 | Some(_) => None, |
517 | None => Some(Self::TraitMethod(TraitImportCandidate { | 539 | None => Some(Self::TraitMethod(TraitImportCandidate { |
518 | receiver_ty: sema.type_of_expr(&method_call.receiver()?)?, | 540 | receiver_ty: sema.type_of_expr(&method_call.receiver()?)?, |
519 | name: NameToImport::Exact(method_call.name_ref()?.to_string()), | 541 | assoc_item_name: NameToImport::Exact(method_call.name_ref()?.to_string()), |
520 | })), | 542 | })), |
521 | } | 543 | } |
522 | } | 544 | } |
@@ -559,10 +581,10 @@ fn path_import_candidate( | |||
559 | qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; | 581 | qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; |
560 | if sema.resolve_path(&qualifier_start_path).is_none() { | 582 | if sema.resolve_path(&qualifier_start_path).is_none() { |
561 | ImportCandidate::Path(PathImportCandidate { | 583 | ImportCandidate::Path(PathImportCandidate { |
562 | qualifier: Qualifier::FirstSegmentUnresolved( | 584 | qualifier: Some(FirstSegmentUnresolved { |
563 | qualifier_start, | 585 | fist_segment: qualifier_start, |
564 | ModPath::from_src_unhygienic(qualifier)?, | 586 | full_qualifier: ModPath::from_src_unhygienic(qualifier)?, |
565 | ), | 587 | }), |
566 | name, | 588 | name, |
567 | }) | 589 | }) |
568 | } else { | 590 | } else { |
@@ -572,12 +594,12 @@ fn path_import_candidate( | |||
572 | Some(PathResolution::Def(ModuleDef::Adt(assoc_item_path))) => { | 594 | Some(PathResolution::Def(ModuleDef::Adt(assoc_item_path))) => { |
573 | ImportCandidate::TraitAssocItem(TraitImportCandidate { | 595 | ImportCandidate::TraitAssocItem(TraitImportCandidate { |
574 | receiver_ty: assoc_item_path.ty(sema.db), | 596 | receiver_ty: assoc_item_path.ty(sema.db), |
575 | name, | 597 | assoc_item_name: name, |
576 | }) | 598 | }) |
577 | } | 599 | } |
578 | Some(_) => return None, | 600 | Some(_) => return None, |
579 | }, | 601 | }, |
580 | None => ImportCandidate::Path(PathImportCandidate { qualifier: Qualifier::Absent, name }), | 602 | None => ImportCandidate::Path(PathImportCandidate { qualifier: None, name }), |
581 | }) | 603 | }) |
582 | } | 604 | } |
583 | 605 | ||