Skip to content

Commit e248299

Browse files
Merge #2982
2982: Merge imports when auto importing r=flodiebold a=SomeoneToIgnore Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
2 parents dce7dc4 + 2ee94e3 commit e248299

File tree

6 files changed

+90
-42
lines changed

6 files changed

+90
-42
lines changed

crates/ra_assists/src/assists/add_import.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use hir::{self, db::HirDatabase};
1+
use hir::{self, db::HirDatabase, ModPath};
22
use ra_syntax::{
33
ast::{self, NameOwner},
44
AstNode, Direction, SmolStr,
@@ -20,10 +20,10 @@ pub fn auto_import_text_edit(
2020
position: &SyntaxNode,
2121
// The statement to use as anchor (last resort)
2222
anchor: &SyntaxNode,
23-
// The path to import as a sequence of strings
24-
target: &[SmolStr],
23+
path_to_import: &ModPath,
2524
edit: &mut TextEditBuilder,
2625
) {
26+
let target = path_to_import.to_string().split("::").map(SmolStr::new).collect::<Vec<_>>();
2727
let container = position.ancestors().find_map(|n| {
2828
if let Some(module) = ast::Module::cast(n.clone()) {
2929
return module.item_list().map(|it| it.syntax().clone());
@@ -32,8 +32,8 @@ pub fn auto_import_text_edit(
3232
});
3333

3434
if let Some(container) = container {
35-
let action = best_action_for_target(container, anchor.clone(), target);
36-
make_assist(&action, target, edit);
35+
let action = best_action_for_target(container, anchor.clone(), &target);
36+
make_assist(&action, &target, edit);
3737
}
3838
}
3939

crates/ra_assists/src/assists/auto_import.rs

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
use hir::db::HirDatabase;
1+
use hir::{db::HirDatabase, ModPath};
22
use ra_syntax::{
33
ast::{self, AstNode},
4-
SmolStr,
54
SyntaxKind::USE_ITEM,
65
SyntaxNode,
76
};
@@ -58,7 +57,6 @@ pub(crate) fn auto_import<F: ImportsLocator>(
5857
.filter_map(|module_def| module_with_name_to_import.find_use_path(ctx.db, module_def))
5958
.filter(|use_path| !use_path.segments.is_empty())
6059
.take(20)
61-
.map(|import| import.to_string())
6260
.collect::<std::collections::BTreeSet<_>>();
6361
if proposed_imports.is_empty() {
6462
return None;
@@ -76,15 +74,10 @@ pub(crate) fn auto_import<F: ImportsLocator>(
7674
)
7775
}
7876

79-
fn import_to_action(import: String, position: &SyntaxNode, anchor: &SyntaxNode) -> ActionBuilder {
77+
fn import_to_action(import: ModPath, position: &SyntaxNode, anchor: &SyntaxNode) -> ActionBuilder {
8078
let mut action_builder = ActionBuilder::default();
8179
action_builder.label(format!("Import `{}`", &import));
82-
auto_import_text_edit(
83-
position,
84-
anchor,
85-
&[SmolStr::new(import)],
86-
action_builder.text_edit_builder(),
87-
);
80+
auto_import_text_edit(position, anchor, &import, action_builder.text_edit_builder());
8881
action_builder
8982
}
9083

@@ -120,6 +113,34 @@ mod tests {
120113
);
121114
}
122115

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+
123144
#[test]
124145
fn applicable_when_found_multiple_imports() {
125146
check_assist_with_imports_locator(

crates/ra_hir/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ pub use hir_def::{
5959
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.
6060
};
6161
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,
6364
};
6465
pub use hir_ty::{display::HirDisplay, CallableDef};

crates/ra_hir_def/src/path.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ use ra_syntax::ast;
1616

1717
use crate::{type_ref::TypeRef, InFile};
1818

19-
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
19+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2020
pub struct ModPath {
2121
pub kind: PathKind,
2222
pub segments: Vec<Name>,
2323
}
2424

25-
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
25+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2626
pub enum PathKind {
2727
Plain,
2828
/// `self::` is `Super(0)`

crates/ra_hir_expand/src/name.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,13 @@ pub mod known {
187187
PartialOrd,
188188
Eq,
189189
PartialEq,
190+
// FIXME delete those after `ImportResolver` is removed.
191+
hash,
192+
fmt,
193+
io,
194+
Display,
195+
Iterator,
196+
Hasher,
190197
);
191198

192199
// self/Self cannot be used as an identifier

crates/ra_ide/src/completion/complete_scope.rs

Lines changed: 43 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use ra_text_edit::TextEditBuilder;
66
use rustc_hash::FxHashMap;
77

88
use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions};
9+
use hir::{ModPath, PathKind};
910

1011
pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) {
1112
if !ctx.is_trivial_path {
@@ -54,58 +55,76 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) {
5455
}
5556
}
5657

57-
fn build_import_label(name: &str, path: &[SmolStr]) -> String {
58+
fn build_import_label(name: &str, path: &ModPath) -> String {
5859
let mut buf = String::with_capacity(64);
5960
buf.push_str(name);
6061
buf.push_str(" (");
61-
fmt_import_path(path, &mut buf);
62+
buf.push_str(&path.to_string());
6263
buf.push_str(")");
6364
buf
6465
}
6566

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-
7767
#[derive(Debug, Clone, Default)]
7868
pub(crate) struct ImportResolver {
7969
// todo: use fst crate or something like that
80-
dummy_names: Vec<(SmolStr, Vec<SmolStr>)>,
70+
dummy_names: Vec<(SmolStr, ModPath)>,
8171
}
8272

8373
impl ImportResolver {
8474
pub(crate) fn new() -> Self {
75+
use hir::name;
76+
8577
let dummy_names = vec![
86-
(SmolStr::new("fmt"), vec![SmolStr::new("std"), SmolStr::new("fmt")]),
87-
(SmolStr::new("io"), vec![SmolStr::new("std"), SmolStr::new("io")]),
88-
(SmolStr::new("iter"), vec![SmolStr::new("std"), SmolStr::new("iter")]),
89-
(SmolStr::new("hash"), vec![SmolStr::new("std"), SmolStr::new("hash")]),
78+
(
79+
SmolStr::new("fmt"),
80+
ModPath { kind: PathKind::Plain, segments: vec![name![std], name![fmt]] },
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+
),
9094
(
9195
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+
},
93100
),
94101
(
95102
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+
},
97107
),
98108
(
99109
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+
},
101114
),
102115
(
103116
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+
},
105121
),
106122
(
107123
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+
},
109128
),
110129
];
111130

@@ -115,7 +134,7 @@ impl ImportResolver {
115134
// Returns a map of importable items filtered by name.
116135
// The map associates item name with its full path.
117136
// 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> {
119138
if name.len() > 1 {
120139
self.dummy_names.iter().filter(|(n, _)| n.contains(name)).cloned().collect()
121140
} else {

0 commit comments

Comments
 (0)