diff options
-rw-r--r-- | crates/ra_assists/src/assists/add_import.rs | 10 | ||||
-rw-r--r-- | crates/ra_assists/src/assists/auto_import.rs | 41 | ||||
-rw-r--r-- | crates/ra_hir/src/lib.rs | 3 | ||||
-rw-r--r-- | crates/ra_hir_def/src/path.rs | 4 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/name.rs | 7 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_scope.rs | 67 |
6 files changed, 90 insertions, 42 deletions
diff --git a/crates/ra_assists/src/assists/add_import.rs b/crates/ra_assists/src/assists/add_import.rs index bf6cfe865..fc038df78 100644 --- a/crates/ra_assists/src/assists/add_import.rs +++ b/crates/ra_assists/src/assists/add_import.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use hir::{self, db::HirDatabase}; | 1 | use hir::{self, db::HirDatabase, ModPath}; |
2 | use ra_syntax::{ | 2 | use ra_syntax::{ |
3 | ast::{self, NameOwner}, | 3 | ast::{self, NameOwner}, |
4 | AstNode, Direction, SmolStr, | 4 | AstNode, Direction, SmolStr, |
@@ -20,10 +20,10 @@ pub fn auto_import_text_edit( | |||
20 | position: &SyntaxNode, | 20 | position: &SyntaxNode, |
21 | // The statement to use as anchor (last resort) | 21 | // The statement to use as anchor (last resort) |
22 | anchor: &SyntaxNode, | 22 | anchor: &SyntaxNode, |
23 | // The path to import as a sequence of strings | 23 | path_to_import: &ModPath, |
24 | target: &[SmolStr], | ||
25 | edit: &mut TextEditBuilder, | 24 | edit: &mut TextEditBuilder, |
26 | ) { | 25 | ) { |
26 | let target = path_to_import.to_string().split("::").map(SmolStr::new).collect::<Vec<_>>(); | ||
27 | let container = position.ancestors().find_map(|n| { | 27 | let container = position.ancestors().find_map(|n| { |
28 | if let Some(module) = ast::Module::cast(n.clone()) { | 28 | if let Some(module) = ast::Module::cast(n.clone()) { |
29 | return module.item_list().map(|it| it.syntax().clone()); | 29 | return module.item_list().map(|it| it.syntax().clone()); |
@@ -32,8 +32,8 @@ pub fn auto_import_text_edit( | |||
32 | }); | 32 | }); |
33 | 33 | ||
34 | if let Some(container) = container { | 34 | if let Some(container) = container { |
35 | let action = best_action_for_target(container, anchor.clone(), target); | 35 | let action = best_action_for_target(container, anchor.clone(), &target); |
36 | make_assist(&action, target, edit); | 36 | make_assist(&action, &target, edit); |
37 | } | 37 | } |
38 | } | 38 | } |
39 | 39 | ||
diff --git a/crates/ra_assists/src/assists/auto_import.rs b/crates/ra_assists/src/assists/auto_import.rs index 8c483e2da..2629f00e6 100644 --- a/crates/ra_assists/src/assists/auto_import.rs +++ b/crates/ra_assists/src/assists/auto_import.rs | |||
@@ -1,7 +1,6 @@ | |||
1 | use hir::db::HirDatabase; | 1 | use hir::{db::HirDatabase, ModPath}; |
2 | use ra_syntax::{ | 2 | use ra_syntax::{ |
3 | ast::{self, AstNode}, | 3 | ast::{self, AstNode}, |
4 | SmolStr, | ||
5 | SyntaxKind::USE_ITEM, | 4 | SyntaxKind::USE_ITEM, |
6 | SyntaxNode, | 5 | SyntaxNode, |
7 | }; | 6 | }; |
@@ -58,7 +57,6 @@ pub(crate) fn auto_import<F: ImportsLocator>( | |||
58 | .filter_map(|module_def| module_with_name_to_import.find_use_path(ctx.db, module_def)) | 57 | .filter_map(|module_def| module_with_name_to_import.find_use_path(ctx.db, module_def)) |
59 | .filter(|use_path| !use_path.segments.is_empty()) | 58 | .filter(|use_path| !use_path.segments.is_empty()) |
60 | .take(20) | 59 | .take(20) |
61 | .map(|import| import.to_string()) | ||
62 | .collect::<std::collections::BTreeSet<_>>(); | 60 | .collect::<std::collections::BTreeSet<_>>(); |
63 | if proposed_imports.is_empty() { | 61 | if proposed_imports.is_empty() { |
64 | return None; | 62 | return None; |
@@ -76,15 +74,10 @@ pub(crate) fn auto_import<F: ImportsLocator>( | |||
76 | ) | 74 | ) |
77 | } | 75 | } |
78 | 76 | ||
79 | fn import_to_action(import: String, position: &SyntaxNode, anchor: &SyntaxNode) -> ActionBuilder { | 77 | fn import_to_action(import: ModPath, position: &SyntaxNode, anchor: &SyntaxNode) -> ActionBuilder { |
80 | let mut action_builder = ActionBuilder::default(); | 78 | let mut action_builder = ActionBuilder::default(); |
81 | action_builder.label(format!("Import `{}`", &import)); | 79 | action_builder.label(format!("Import `{}`", &import)); |
82 | auto_import_text_edit( | 80 | auto_import_text_edit(position, anchor, &import, action_builder.text_edit_builder()); |
83 | position, | ||
84 | anchor, | ||
85 | &[SmolStr::new(import)], | ||
86 | action_builder.text_edit_builder(), | ||
87 | ); | ||
88 | action_builder | 81 | action_builder |
89 | } | 82 | } |
90 | 83 | ||
@@ -121,6 +114,34 @@ mod tests { | |||
121 | } | 114 | } |
122 | 115 | ||
123 | #[test] | 116 | #[test] |
117 | fn auto_imports_are_merged() { | ||
118 | check_assist_with_imports_locator( | ||
119 | auto_import, | ||
120 | TestImportsLocator::new, | ||
121 | r" | ||
122 | use PubMod::PubStruct1; | ||
123 | |||
124 | PubStruct2<|> | ||
125 | |||
126 | pub mod PubMod { | ||
127 | pub struct PubStruct1; | ||
128 | pub struct PubStruct2; | ||
129 | } | ||
130 | ", | ||
131 | r" | ||
132 | use PubMod::{PubStruct2, PubStruct1}; | ||
133 | |||
134 | PubStruct2<|> | ||
135 | |||
136 | pub mod PubMod { | ||
137 | pub struct PubStruct1; | ||
138 | pub struct PubStruct2; | ||
139 | } | ||
140 | ", | ||
141 | ); | ||
142 | } | ||
143 | |||
144 | #[test] | ||
124 | fn applicable_when_found_multiple_imports() { | 145 | fn applicable_when_found_multiple_imports() { |
125 | check_assist_with_imports_locator( | 146 | check_assist_with_imports_locator( |
126 | auto_import, | 147 | auto_import, |
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 9e2673d13..ea06a4a58 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs | |||
@@ -59,6 +59,7 @@ pub use hir_def::{ | |||
59 | ModuleDefId, // FIXME this is exposed and should be used for implementing the `TestImportsLocator` in `ra_assists` only, should be removed later along with the trait and the implementation. | 59 | ModuleDefId, // FIXME this is exposed and should be used for implementing the `TestImportsLocator` in `ra_assists` only, should be removed later along with the trait and the implementation. |
60 | }; | 60 | }; |
61 | pub use hir_expand::{ | 61 | pub use hir_expand::{ |
62 | name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, MacroDefId, MacroFile, Origin, | 62 | name::{name, Name}, |
63 | HirFileId, InFile, MacroCallId, MacroCallLoc, MacroDefId, MacroFile, Origin, | ||
63 | }; | 64 | }; |
64 | pub use hir_ty::{display::HirDisplay, CallableDef}; | 65 | pub use hir_ty::{display::HirDisplay, CallableDef}; |
diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs index ab290e2c9..a150b899f 100644 --- a/crates/ra_hir_def/src/path.rs +++ b/crates/ra_hir_def/src/path.rs | |||
@@ -16,13 +16,13 @@ use ra_syntax::ast; | |||
16 | 16 | ||
17 | use crate::{type_ref::TypeRef, InFile}; | 17 | use crate::{type_ref::TypeRef, InFile}; |
18 | 18 | ||
19 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 19 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] |
20 | pub struct ModPath { | 20 | pub struct ModPath { |
21 | pub kind: PathKind, | 21 | pub kind: PathKind, |
22 | pub segments: Vec<Name>, | 22 | pub segments: Vec<Name>, |
23 | } | 23 | } |
24 | 24 | ||
25 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 25 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] |
26 | pub enum PathKind { | 26 | pub enum PathKind { |
27 | Plain, | 27 | Plain, |
28 | /// `self::` is `Super(0)` | 28 | /// `self::` is `Super(0)` |
diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs index b2e10f445..133805bdb 100644 --- a/crates/ra_hir_expand/src/name.rs +++ b/crates/ra_hir_expand/src/name.rs | |||
@@ -187,6 +187,13 @@ pub mod known { | |||
187 | PartialOrd, | 187 | PartialOrd, |
188 | Eq, | 188 | Eq, |
189 | PartialEq, | 189 | PartialEq, |
190 | // FIXME delete those after `ImportResolver` is removed. | ||
191 | hash, | ||
192 | fmt, | ||
193 | io, | ||
194 | Display, | ||
195 | Iterator, | ||
196 | Hasher, | ||
190 | ); | 197 | ); |
191 | 198 | ||
192 | // self/Self cannot be used as an identifier | 199 | // self/Self cannot be used as an identifier |
diff --git a/crates/ra_ide/src/completion/complete_scope.rs b/crates/ra_ide/src/completion/complete_scope.rs index 458d7525e..64b04ec2b 100644 --- a/crates/ra_ide/src/completion/complete_scope.rs +++ b/crates/ra_ide/src/completion/complete_scope.rs | |||
@@ -6,6 +6,7 @@ use ra_text_edit::TextEditBuilder; | |||
6 | use rustc_hash::FxHashMap; | 6 | use rustc_hash::FxHashMap; |
7 | 7 | ||
8 | use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions}; | 8 | use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions}; |
9 | use hir::{ModPath, PathKind}; | ||
9 | 10 | ||
10 | pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { | 11 | pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { |
11 | if !ctx.is_trivial_path { | 12 | if !ctx.is_trivial_path { |
@@ -54,58 +55,76 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { | |||
54 | } | 55 | } |
55 | } | 56 | } |
56 | 57 | ||
57 | fn build_import_label(name: &str, path: &[SmolStr]) -> String { | 58 | fn build_import_label(name: &str, path: &ModPath) -> String { |
58 | let mut buf = String::with_capacity(64); | 59 | let mut buf = String::with_capacity(64); |
59 | buf.push_str(name); | 60 | buf.push_str(name); |
60 | buf.push_str(" ("); | 61 | buf.push_str(" ("); |
61 | fmt_import_path(path, &mut buf); | 62 | buf.push_str(&path.to_string()); |
62 | buf.push_str(")"); | 63 | buf.push_str(")"); |
63 | buf | 64 | buf |
64 | } | 65 | } |
65 | 66 | ||
66 | fn fmt_import_path(path: &[SmolStr], buf: &mut String) { | ||
67 | let mut segments = path.iter(); | ||
68 | if let Some(s) = segments.next() { | ||
69 | buf.push_str(&s); | ||
70 | } | ||
71 | for s in segments { | ||
72 | buf.push_str("::"); | ||
73 | buf.push_str(&s); | ||
74 | } | ||
75 | } | ||
76 | |||
77 | #[derive(Debug, Clone, Default)] | 67 | #[derive(Debug, Clone, Default)] |
78 | pub(crate) struct ImportResolver { | 68 | pub(crate) struct ImportResolver { |
79 | // todo: use fst crate or something like that | 69 | // todo: use fst crate or something like that |
80 | dummy_names: Vec<(SmolStr, Vec<SmolStr>)>, | 70 | dummy_names: Vec<(SmolStr, ModPath)>, |
81 | } | 71 | } |
82 | 72 | ||
83 | impl ImportResolver { | 73 | impl ImportResolver { |
84 | pub(crate) fn new() -> Self { | 74 | pub(crate) fn new() -> Self { |
75 | use hir::name; | ||
76 | |||
85 | let dummy_names = vec![ | 77 | let dummy_names = vec![ |
86 | (SmolStr::new("fmt"), vec![SmolStr::new("std"), SmolStr::new("fmt")]), | 78 | ( |
87 | (SmolStr::new("io"), vec![SmolStr::new("std"), SmolStr::new("io")]), | 79 | SmolStr::new("fmt"), |
88 | (SmolStr::new("iter"), vec![SmolStr::new("std"), SmolStr::new("iter")]), | 80 | ModPath { kind: PathKind::Plain, segments: vec![name![std], name![fmt]] }, |
89 | (SmolStr::new("hash"), vec![SmolStr::new("std"), SmolStr::new("hash")]), | 81 | ), |
82 | ( | ||
83 | SmolStr::new("io"), | ||
84 | ModPath { kind: PathKind::Plain, segments: vec![name![std], name![io]] }, | ||
85 | ), | ||
86 | ( | ||
87 | SmolStr::new("iter"), | ||
88 | ModPath { kind: PathKind::Plain, segments: vec![name![std], name![iter]] }, | ||
89 | ), | ||
90 | ( | ||
91 | SmolStr::new("hash"), | ||
92 | ModPath { kind: PathKind::Plain, segments: vec![name![std], name![hash]] }, | ||
93 | ), | ||
90 | ( | 94 | ( |
91 | SmolStr::new("Debug"), | 95 | SmolStr::new("Debug"), |
92 | vec![SmolStr::new("std"), SmolStr::new("fmt"), SmolStr::new("Debug")], | 96 | ModPath { |
97 | kind: PathKind::Plain, | ||
98 | segments: vec![name![std], name![fmt], name![Debug]], | ||
99 | }, | ||
93 | ), | 100 | ), |
94 | ( | 101 | ( |
95 | SmolStr::new("Display"), | 102 | SmolStr::new("Display"), |
96 | vec![SmolStr::new("std"), SmolStr::new("fmt"), SmolStr::new("Display")], | 103 | ModPath { |
104 | kind: PathKind::Plain, | ||
105 | segments: vec![name![std], name![fmt], name![Display]], | ||
106 | }, | ||
97 | ), | 107 | ), |
98 | ( | 108 | ( |
99 | SmolStr::new("Hash"), | 109 | SmolStr::new("Hash"), |
100 | vec![SmolStr::new("std"), SmolStr::new("hash"), SmolStr::new("Hash")], | 110 | ModPath { |
111 | kind: PathKind::Plain, | ||
112 | segments: vec![name![std], name![hash], name![Hash]], | ||
113 | }, | ||
101 | ), | 114 | ), |
102 | ( | 115 | ( |
103 | SmolStr::new("Hasher"), | 116 | SmolStr::new("Hasher"), |
104 | vec![SmolStr::new("std"), SmolStr::new("hash"), SmolStr::new("Hasher")], | 117 | ModPath { |
118 | kind: PathKind::Plain, | ||
119 | segments: vec![name![std], name![hash], name![Hasher]], | ||
120 | }, | ||
105 | ), | 121 | ), |
106 | ( | 122 | ( |
107 | SmolStr::new("Iterator"), | 123 | SmolStr::new("Iterator"), |
108 | vec![SmolStr::new("std"), SmolStr::new("iter"), SmolStr::new("Iterator")], | 124 | ModPath { |
125 | kind: PathKind::Plain, | ||
126 | segments: vec![name![std], name![iter], name![Iterator]], | ||
127 | }, | ||
109 | ), | 128 | ), |
110 | ]; | 129 | ]; |
111 | 130 | ||
@@ -115,7 +134,7 @@ impl ImportResolver { | |||
115 | // Returns a map of importable items filtered by name. | 134 | // Returns a map of importable items filtered by name. |
116 | // The map associates item name with its full path. | 135 | // The map associates item name with its full path. |
117 | // todo: should return Resolutions | 136 | // todo: should return Resolutions |
118 | pub(crate) fn all_names(&self, name: &str) -> FxHashMap<SmolStr, Vec<SmolStr>> { | 137 | pub(crate) fn all_names(&self, name: &str) -> FxHashMap<SmolStr, ModPath> { |
119 | if name.len() > 1 { | 138 | if name.len() > 1 { |
120 | self.dummy_names.iter().filter(|(n, _)| n.contains(name)).cloned().collect() | 139 | self.dummy_names.iter().filter(|(n, _)| n.contains(name)).cloned().collect() |
121 | } else { | 140 | } else { |