aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/assists/src/assist_config.rs8
-rw-r--r--crates/assists/src/handlers/auto_import.rs9
-rw-r--r--crates/assists/src/handlers/qualify_path.rs40
-rw-r--r--crates/assists/src/handlers/unmerge_use.rs231
-rw-r--r--crates/assists/src/lib.rs4
-rw-r--r--crates/assists/src/tests.rs9
-rw-r--r--crates/assists/src/tests/generated.rs14
-rw-r--r--crates/assists/src/utils.rs1
-rw-r--r--crates/completion/src/completions.rs1
-rw-r--r--crates/completion/src/completions/flyimport.rs291
-rw-r--r--crates/completion/src/completions/keyword.rs2
-rw-r--r--crates/completion/src/completions/unqualified_path.rs251
-rw-r--r--crates/completion/src/config.rs4
-rw-r--r--crates/completion/src/item.rs4
-rw-r--r--crates/completion/src/lib.rs5
-rw-r--r--crates/completion/src/render.rs15
-rw-r--r--crates/completion/src/test_utils.rs14
-rw-r--r--crates/hir_def/Cargo.toml2
-rw-r--r--crates/hir_def/src/adt.rs2
-rw-r--r--crates/hir_def/src/attr.rs2
-rw-r--r--crates/hir_def/src/body.rs2
-rw-r--r--crates/hir_def/src/db.rs2
-rw-r--r--crates/hir_def/src/expr.rs4
-rw-r--r--crates/hir_def/src/generics.rs2
-rw-r--r--crates/hir_def/src/item_tree.rs2
-rw-r--r--crates/hir_def/src/item_tree/lower.rs4
-rw-r--r--crates/hir_def/src/src.rs2
-rw-r--r--crates/hir_def/src/trace.rs4
-rw-r--r--crates/hir_expand/Cargo.toml2
-rw-r--r--crates/hir_expand/src/ast_id_map.rs30
-rw-r--r--crates/hir_expand/src/proc_macro.rs4
-rw-r--r--crates/hir_ty/Cargo.toml2
-rw-r--r--crates/hir_ty/src/db.rs2
-rw-r--r--crates/hir_ty/src/infer.rs2
-rw-r--r--crates/hir_ty/src/lower.rs2
-rw-r--r--crates/ide/src/lib.rs15
-rw-r--r--crates/ide/src/syntax_tree.rs28
-rw-r--r--crates/ide_db/src/helpers.rs1
-rw-r--r--crates/ide_db/src/helpers/import_assets.rs (renamed from crates/assists/src/utils/import_assets.rs)50
-rw-r--r--crates/ide_db/src/helpers/insert_use.rs8
-rw-r--r--crates/ide_db/src/helpers/insert_use/tests.rs2
-rw-r--r--crates/ide_db/src/imports_locator.rs6
-rw-r--r--crates/mbe/src/tests.rs10
-rw-r--r--crates/parser/src/syntax_kind.rs3
-rw-r--r--crates/proc_macro_api/Cargo.toml2
-rw-r--r--crates/proc_macro_srv/src/tests/mod.rs8
-rw-r--r--crates/proc_macro_srv/src/tests/utils.rs2
-rw-r--r--crates/profile/Cargo.toml2
-rw-r--r--crates/project_model/Cargo.toml3
-rw-r--r--crates/project_model/src/lib.rs1
-rw-r--r--crates/project_model/src/rustc_cfg.rs34
-rw-r--r--crates/project_model/src/workspace.rs121
-rw-r--r--crates/rust-analyzer/src/cli/analysis_bench.rs5
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs2
-rw-r--r--crates/rust-analyzer/src/config.rs33
-rw-r--r--crates/rust-analyzer/src/reload.rs16
-rw-r--r--crates/rust-analyzer/src/to_proto.rs5
-rw-r--r--crates/syntax/Cargo.toml2
-rw-r--r--crates/syntax/src/ast/make.rs8
59 files changed, 866 insertions, 476 deletions
diff --git a/crates/assists/src/assist_config.rs b/crates/assists/src/assist_config.rs
index 4fe8ea761..9cabf037c 100644
--- a/crates/assists/src/assist_config.rs
+++ b/crates/assists/src/assist_config.rs
@@ -4,7 +4,7 @@
4//! module, and we use to statically check that we only produce snippet 4//! module, and we use to statically check that we only produce snippet
5//! assists if we are allowed to. 5//! assists if we are allowed to.
6 6
7use ide_db::helpers::{insert_use::MergeBehavior, SnippetCap}; 7use ide_db::helpers::{insert_use::InsertUseConfig, SnippetCap};
8 8
9use crate::AssistKind; 9use crate::AssistKind;
10 10
@@ -14,9 +14,3 @@ pub struct AssistConfig {
14 pub allowed: Option<Vec<AssistKind>>, 14 pub allowed: Option<Vec<AssistKind>>,
15 pub insert_use: InsertUseConfig, 15 pub insert_use: InsertUseConfig,
16} 16}
17
18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
19pub struct InsertUseConfig {
20 pub merge: Option<MergeBehavior>,
21 pub prefix_kind: hir::PrefixKind,
22}
diff --git a/crates/assists/src/handlers/auto_import.rs b/crates/assists/src/handlers/auto_import.rs
index 55620f0f3..4e2a4fcd9 100644
--- a/crates/assists/src/handlers/auto_import.rs
+++ b/crates/assists/src/handlers/auto_import.rs
@@ -1,13 +1,11 @@
1use ide_db::helpers::{ 1use ide_db::helpers::{
2 import_assets::{ImportAssets, ImportCandidate},
2 insert_use::{insert_use, ImportScope}, 3 insert_use::{insert_use, ImportScope},
3 mod_path_to_ast, 4 mod_path_to_ast,
4}; 5};
5use syntax::ast; 6use syntax::ast;
6 7
7use crate::{ 8use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
8 utils::import_assets::{ImportAssets, ImportCandidate},
9 AssistContext, AssistId, AssistKind, Assists, GroupLabel,
10};
11 9
12// Feature: Auto Import 10// Feature: Auto Import
13// 11//
@@ -121,8 +119,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
121 119
122fn import_group_message(import_candidate: &ImportCandidate) -> GroupLabel { 120fn import_group_message(import_candidate: &ImportCandidate) -> GroupLabel {
123 let name = match import_candidate { 121 let name = match import_candidate {
124 ImportCandidate::UnqualifiedName(candidate) 122 ImportCandidate::Path(candidate) => format!("Import {}", &candidate.name),
125 | ImportCandidate::QualifierStart(candidate) => format!("Import {}", &candidate.name),
126 ImportCandidate::TraitAssocItem(candidate) => { 123 ImportCandidate::TraitAssocItem(candidate) => {
127 format!("Import a trait for item {}", &candidate.name) 124 format!("Import a trait for item {}", &candidate.name)
128 } 125 }
diff --git a/crates/assists/src/handlers/qualify_path.rs b/crates/assists/src/handlers/qualify_path.rs
index f7fbf37f4..a7d9fd4dc 100644
--- a/crates/assists/src/handlers/qualify_path.rs
+++ b/crates/assists/src/handlers/qualify_path.rs
@@ -1,7 +1,10 @@
1use std::iter; 1use std::iter;
2 2
3use hir::AsName; 3use hir::AsName;
4use ide_db::helpers::mod_path_to_ast; 4use ide_db::helpers::{
5 import_assets::{ImportAssets, ImportCandidate},
6 mod_path_to_ast,
7};
5use ide_db::RootDatabase; 8use ide_db::RootDatabase;
6use syntax::{ 9use syntax::{
7 ast, 10 ast,
@@ -12,7 +15,6 @@ use test_utils::mark;
12 15
13use crate::{ 16use crate::{
14 assist_context::{AssistContext, Assists}, 17 assist_context::{AssistContext, Assists},
15 utils::import_assets::{ImportAssets, ImportCandidate},
16 AssistId, AssistKind, GroupLabel, 18 AssistId, AssistKind, GroupLabel,
17}; 19};
18 20
@@ -53,17 +55,18 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
53 let range = ctx.sema.original_range(import_assets.syntax_under_caret()).range; 55 let range = ctx.sema.original_range(import_assets.syntax_under_caret()).range;
54 56
55 let qualify_candidate = match candidate { 57 let qualify_candidate = match candidate {
56 ImportCandidate::QualifierStart(_) => { 58 ImportCandidate::Path(candidate) => {
57 mark::hit!(qualify_path_qualifier_start); 59 if candidate.qualifier.is_some() {
58 let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?; 60 mark::hit!(qualify_path_qualifier_start);
59 let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?); 61 let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?;
60 QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list()) 62 let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?);
61 } 63 QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list())
62 ImportCandidate::UnqualifiedName(_) => { 64 } else {
63 mark::hit!(qualify_path_unqualified_name); 65 mark::hit!(qualify_path_unqualified_name);
64 let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?; 66 let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?;
65 let generics = path.segment()?.generic_arg_list(); 67 let generics = path.segment()?.generic_arg_list();
66 QualifyCandidate::UnqualifiedName(generics) 68 QualifyCandidate::UnqualifiedName(generics)
69 }
67 } 70 }
68 ImportCandidate::TraitAssocItem(_) => { 71 ImportCandidate::TraitAssocItem(_) => {
69 mark::hit!(qualify_path_trait_assoc_item); 72 mark::hit!(qualify_path_trait_assoc_item);
@@ -186,7 +189,7 @@ fn item_as_trait(item: hir::ItemInNs) -> Option<hir::Trait> {
186 189
187fn group_label(candidate: &ImportCandidate) -> GroupLabel { 190fn group_label(candidate: &ImportCandidate) -> GroupLabel {
188 let name = match candidate { 191 let name = match candidate {
189 ImportCandidate::UnqualifiedName(it) | ImportCandidate::QualifierStart(it) => &it.name, 192 ImportCandidate::Path(it) => &it.name,
190 ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => &it.name, 193 ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => &it.name,
191 }; 194 };
192 GroupLabel(format!("Qualify {}", name)) 195 GroupLabel(format!("Qualify {}", name))
@@ -194,8 +197,13 @@ fn group_label(candidate: &ImportCandidate) -> GroupLabel {
194 197
195fn label(candidate: &ImportCandidate, import: &hir::ModPath) -> String { 198fn label(candidate: &ImportCandidate, import: &hir::ModPath) -> String {
196 match candidate { 199 match candidate {
197 ImportCandidate::UnqualifiedName(_) => format!("Qualify as `{}`", &import), 200 ImportCandidate::Path(candidate) => {
198 ImportCandidate::QualifierStart(_) => format!("Qualify with `{}`", &import), 201 if candidate.qualifier.is_some() {
202 format!("Qualify with `{}`", &import)
203 } else {
204 format!("Qualify as `{}`", &import)
205 }
206 }
199 ImportCandidate::TraitAssocItem(_) => format!("Qualify `{}`", &import), 207 ImportCandidate::TraitAssocItem(_) => format!("Qualify `{}`", &import),
200 ImportCandidate::TraitMethod(_) => format!("Qualify with cast as `{}`", &import), 208 ImportCandidate::TraitMethod(_) => format!("Qualify with cast as `{}`", &import),
201 } 209 }
diff --git a/crates/assists/src/handlers/unmerge_use.rs b/crates/assists/src/handlers/unmerge_use.rs
new file mode 100644
index 000000000..3dbef8e51
--- /dev/null
+++ b/crates/assists/src/handlers/unmerge_use.rs
@@ -0,0 +1,231 @@
1use syntax::{
2 algo::SyntaxRewriter,
3 ast::{self, edit::AstNodeEdit, VisibilityOwner},
4 AstNode, SyntaxKind,
5};
6use test_utils::mark;
7
8use crate::{
9 assist_context::{AssistContext, Assists},
10 AssistId, AssistKind,
11};
12
13// Assist: unmerge_use
14//
15// Extracts single use item from use list.
16//
17// ```
18// use std::fmt::{Debug, Display$0};
19// ```
20// ->
21// ```
22// use std::fmt::{Debug};
23// use std::fmt::Display;
24// ```
25pub(crate) fn unmerge_use(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
26 let tree: ast::UseTree = ctx.find_node_at_offset()?;
27
28 let tree_list = tree.syntax().parent().and_then(ast::UseTreeList::cast)?;
29 if tree_list.use_trees().count() < 2 {
30 mark::hit!(skip_single_use_item);
31 return None;
32 }
33
34 let use_: ast::Use = tree_list.syntax().ancestors().find_map(ast::Use::cast)?;
35 let path = resolve_full_path(&tree)?;
36
37 let target = tree.syntax().text_range();
38 acc.add(
39 AssistId("unmerge_use", AssistKind::RefactorRewrite),
40 "Unmerge use",
41 target,
42 |builder| {
43 let new_use = ast::make::use_(
44 use_.visibility(),
45 ast::make::use_tree(
46 path,
47 tree.use_tree_list(),
48 tree.rename(),
49 tree.star_token().is_some(),
50 ),
51 );
52
53 let mut rewriter = SyntaxRewriter::default();
54 rewriter += tree.remove();
55 rewriter.insert_after(use_.syntax(), &ast::make::tokens::single_newline());
56 if let ident_level @ 1..=usize::MAX = use_.indent_level().0 as usize {
57 rewriter.insert_after(
58 use_.syntax(),
59 &ast::make::tokens::whitespace(&" ".repeat(4 * ident_level)),
60 );
61 }
62 rewriter.insert_after(use_.syntax(), new_use.syntax());
63
64 builder.rewrite(rewriter);
65 },
66 )
67}
68
69fn resolve_full_path(tree: &ast::UseTree) -> Option<ast::Path> {
70 let mut paths = tree
71 .syntax()
72 .ancestors()
73 .take_while(|n| n.kind() != SyntaxKind::USE_KW)
74 .filter_map(ast::UseTree::cast)
75 .filter_map(|t| t.path());
76
77 let mut final_path = paths.next()?;
78 for path in paths {
79 final_path = ast::make::path_concat(path, final_path)
80 }
81 Some(final_path)
82}
83
84#[cfg(test)]
85mod tests {
86 use crate::tests::{check_assist, check_assist_not_applicable};
87
88 use super::*;
89
90 #[test]
91 fn skip_single_use_item() {
92 mark::check!(skip_single_use_item);
93 check_assist_not_applicable(
94 unmerge_use,
95 r"
96use std::fmt::Debug$0;
97",
98 );
99 check_assist_not_applicable(
100 unmerge_use,
101 r"
102use std::fmt::{Debug$0};
103",
104 );
105 check_assist_not_applicable(
106 unmerge_use,
107 r"
108use std::fmt::Debug as Dbg$0;
109",
110 );
111 }
112
113 #[test]
114 fn skip_single_glob_import() {
115 check_assist_not_applicable(
116 unmerge_use,
117 r"
118use std::fmt::*$0;
119",
120 );
121 }
122
123 #[test]
124 fn unmerge_use_item() {
125 check_assist(
126 unmerge_use,
127 r"
128use std::fmt::{Debug, Display$0};
129",
130 r"
131use std::fmt::{Debug};
132use std::fmt::Display;
133",
134 );
135
136 check_assist(
137 unmerge_use,
138 r"
139use std::fmt::{Debug, format$0, Display};
140",
141 r"
142use std::fmt::{Debug, Display};
143use std::fmt::format;
144",
145 );
146 }
147
148 #[test]
149 fn unmerge_glob_import() {
150 check_assist(
151 unmerge_use,
152 r"
153use std::fmt::{*$0, Display};
154",
155 r"
156use std::fmt::{Display};
157use std::fmt::*;
158",
159 );
160 }
161
162 #[test]
163 fn unmerge_renamed_use_item() {
164 check_assist(
165 unmerge_use,
166 r"
167use std::fmt::{Debug, Display as Disp$0};
168",
169 r"
170use std::fmt::{Debug};
171use std::fmt::Display as Disp;
172",
173 );
174 }
175
176 #[test]
177 fn unmerge_indented_use_item() {
178 check_assist(
179 unmerge_use,
180 r"
181mod format {
182 use std::fmt::{Debug, Display$0 as Disp, format};
183}
184",
185 r"
186mod format {
187 use std::fmt::{Debug, format};
188 use std::fmt::Display as Disp;
189}
190",
191 );
192 }
193
194 #[test]
195 fn unmerge_nested_use_item() {
196 check_assist(
197 unmerge_use,
198 r"
199use foo::bar::{baz::{qux$0, foobar}, barbaz};
200",
201 r"
202use foo::bar::{baz::{foobar}, barbaz};
203use foo::bar::baz::qux;
204",
205 );
206 check_assist(
207 unmerge_use,
208 r"
209use foo::bar::{baz$0::{qux, foobar}, barbaz};
210",
211 r"
212use foo::bar::{barbaz};
213use foo::bar::baz::{qux, foobar};
214",
215 );
216 }
217
218 #[test]
219 fn unmerge_use_item_with_visibility() {
220 check_assist(
221 unmerge_use,
222 r"
223pub use std::fmt::{Debug, Display$0};
224",
225 r"
226pub use std::fmt::{Debug};
227pub use std::fmt::Display;
228",
229 );
230 }
231}
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs
index 1080294ab..14178a651 100644
--- a/crates/assists/src/lib.rs
+++ b/crates/assists/src/lib.rs
@@ -24,7 +24,7 @@ use syntax::TextRange;
24 24
25pub(crate) use crate::assist_context::{AssistContext, Assists}; 25pub(crate) use crate::assist_context::{AssistContext, Assists};
26 26
27pub use assist_config::{AssistConfig, InsertUseConfig}; 27pub use assist_config::AssistConfig;
28 28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)] 29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub enum AssistKind { 30pub enum AssistKind {
@@ -156,6 +156,7 @@ mod handlers {
156 mod replace_unwrap_with_match; 156 mod replace_unwrap_with_match;
157 mod split_import; 157 mod split_import;
158 mod toggle_ignore; 158 mod toggle_ignore;
159 mod unmerge_use;
159 mod unwrap_block; 160 mod unwrap_block;
160 mod wrap_return_type_in_result; 161 mod wrap_return_type_in_result;
161 162
@@ -213,6 +214,7 @@ mod handlers {
213 replace_unwrap_with_match::replace_unwrap_with_match, 214 replace_unwrap_with_match::replace_unwrap_with_match,
214 split_import::split_import, 215 split_import::split_import,
215 toggle_ignore::toggle_ignore, 216 toggle_ignore::toggle_ignore,
217 unmerge_use::unmerge_use,
216 unwrap_block::unwrap_block, 218 unwrap_block::unwrap_block,
217 wrap_return_type_in_result::wrap_return_type_in_result, 219 wrap_return_type_in_result::wrap_return_type_in_result,
218 // These are manually sorted for better priorities 220 // These are manually sorted for better priorities
diff --git a/crates/assists/src/tests.rs b/crates/assists/src/tests.rs
index 71431b406..32bd8698b 100644
--- a/crates/assists/src/tests.rs
+++ b/crates/assists/src/tests.rs
@@ -3,16 +3,17 @@ mod generated;
3use hir::Semantics; 3use hir::Semantics;
4use ide_db::{ 4use ide_db::{
5 base_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt}, 5 base_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt},
6 helpers::{insert_use::MergeBehavior, SnippetCap}, 6 helpers::{
7 insert_use::{InsertUseConfig, MergeBehavior},
8 SnippetCap,
9 },
7 source_change::FileSystemEdit, 10 source_change::FileSystemEdit,
8 RootDatabase, 11 RootDatabase,
9}; 12};
10use syntax::TextRange; 13use syntax::TextRange;
11use test_utils::{assert_eq_text, extract_offset, extract_range}; 14use test_utils::{assert_eq_text, extract_offset, extract_range};
12 15
13use crate::{ 16use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, Assists};
14 handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, Assists, InsertUseConfig,
15};
16use stdx::{format_to, trim_indent}; 17use stdx::{format_to, trim_indent};
17 18
18pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig { 19pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig {
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs
index 217f577eb..d48d063b4 100644
--- a/crates/assists/src/tests/generated.rs
+++ b/crates/assists/src/tests/generated.rs
@@ -1138,6 +1138,20 @@ fn arithmetics {
1138} 1138}
1139 1139
1140#[test] 1140#[test]
1141fn doctest_unmerge_use() {
1142 check_doc_test(
1143 "unmerge_use",
1144 r#####"
1145use std::fmt::{Debug, Display$0};
1146"#####,
1147 r#####"
1148use std::fmt::{Debug};
1149use std::fmt::Display;
1150"#####,
1151 )
1152}
1153
1154#[test]
1141fn doctest_unwrap_block() { 1155fn doctest_unwrap_block() {
1142 check_doc_test( 1156 check_doc_test(
1143 "unwrap_block", 1157 "unwrap_block",
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs
index 9ea96eb73..fc9f83bab 100644
--- a/crates/assists/src/utils.rs
+++ b/crates/assists/src/utils.rs
@@ -1,5 +1,4 @@
1//! Assorted functions shared by several assists. 1//! Assorted functions shared by several assists.
2pub(crate) mod import_assets;
3 2
4use std::ops; 3use std::ops;
5 4
diff --git a/crates/completion/src/completions.rs b/crates/completion/src/completions.rs
index 00c9e76f0..c3ce6e51d 100644
--- a/crates/completion/src/completions.rs
+++ b/crates/completion/src/completions.rs
@@ -13,6 +13,7 @@ pub(crate) mod postfix;
13pub(crate) mod macro_in_item_position; 13pub(crate) mod macro_in_item_position;
14pub(crate) mod trait_impl; 14pub(crate) mod trait_impl;
15pub(crate) mod mod_; 15pub(crate) mod mod_;
16pub(crate) mod flyimport;
16 17
17use hir::{ModPath, ScopeDef, Type}; 18use hir::{ModPath, ScopeDef, Type};
18 19
diff --git a/crates/completion/src/completions/flyimport.rs b/crates/completion/src/completions/flyimport.rs
new file mode 100644
index 000000000..222809638
--- /dev/null
+++ b/crates/completion/src/completions/flyimport.rs
@@ -0,0 +1,291 @@
1//! Feature: completion with imports-on-the-fly
2//!
3//! When completing names in the current scope, proposes additional imports from other modules or crates,
4//! if they can be qualified in the scope and their name contains all symbols from the completion input
5//! (case-insensitive, in any order or places).
6//!
7//! ```
8//! fn main() {
9//! pda$0
10//! }
11//! # pub mod std { pub mod marker { pub struct PhantomData { } } }
12//! ```
13//! ->
14//! ```
15//! use std::marker::PhantomData;
16//!
17//! fn main() {
18//! PhantomData
19//! }
20//! # pub mod std { pub mod marker { pub struct PhantomData { } } }
21//! ```
22//!
23//! .Fuzzy search details
24//!
25//! To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only
26//! (i.e. in `HashMap` in the `std::collections::HashMap` path).
27//! For the same reasons, avoids searching for any imports for inputs with their length less that 2 symbols.
28//!
29//! .Import configuration
30//!
31//! It is possible to configure how use-trees are merged with the `importMergeBehavior` setting.
32//! Mimics the corresponding behavior of the `Auto Import` feature.
33//!
34//! .LSP and performance implications
35//!
36//! The feature is enabled only if the LSP client supports LSP protocol version 3.16+ and reports the `additionalTextEdits`
37//! (case sensitive) resolve client capability in its client capabilities.
38//! This way the server is able to defer the costly computations, doing them for a selected completion item only.
39//! For clients with no such support, all edits have to be calculated on the completion request, including the fuzzy search completion ones,
40//! which might be slow ergo the feature is automatically disabled.
41//!
42//! .Feature toggle
43//!
44//! The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.enableAutoimportCompletions` flag.
45//! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding
46//! capability enabled.
47
48use either::Either;
49use hir::{ModPath, ScopeDef};
50use ide_db::{helpers::insert_use::ImportScope, imports_locator};
51use syntax::AstNode;
52use test_utils::mark;
53
54use crate::{
55 context::CompletionContext,
56 render::{render_resolution_with_import, RenderContext},
57 ImportEdit,
58};
59
60use super::Completions;
61
62pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
63 if !ctx.config.enable_autoimport_completions {
64 return None;
65 }
66 if ctx.attribute_under_caret.is_some() || ctx.mod_declaration_under_caret.is_some() {
67 return None;
68 }
69 let potential_import_name = ctx.token.to_string();
70 if potential_import_name.len() < 2 {
71 return None;
72 }
73 let _p = profile::span("import_on_the_fly").detail(|| potential_import_name.to_string());
74
75 let current_module = ctx.scope.module()?;
76 let anchor = ctx.name_ref_syntax.as_ref()?;
77 let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?;
78
79 let user_input_lowercased = potential_import_name.to_lowercase();
80 let mut all_mod_paths = imports_locator::find_similar_imports(
81 &ctx.sema,
82 ctx.krate?,
83 Some(40),
84 potential_import_name,
85 true,
86 true,
87 )
88 .filter_map(|import_candidate| {
89 Some(match import_candidate {
90 Either::Left(module_def) => {
91 (current_module.find_use_path(ctx.db, module_def)?, ScopeDef::ModuleDef(module_def))
92 }
93 Either::Right(macro_def) => {
94 (current_module.find_use_path(ctx.db, macro_def)?, ScopeDef::MacroDef(macro_def))
95 }
96 })
97 })
98 .filter(|(mod_path, _)| mod_path.len() > 1)
99 .collect::<Vec<_>>();
100
101 all_mod_paths.sort_by_cached_key(|(mod_path, _)| {
102 compute_fuzzy_completion_order_key(mod_path, &user_input_lowercased)
103 });
104
105 acc.add_all(all_mod_paths.into_iter().filter_map(|(import_path, definition)| {
106 render_resolution_with_import(
107 RenderContext::new(ctx),
108 ImportEdit { import_path, import_scope: import_scope.clone() },
109 &definition,
110 )
111 }));
112 Some(())
113}
114
115fn compute_fuzzy_completion_order_key(
116 proposed_mod_path: &ModPath,
117 user_input_lowercased: &str,
118) -> usize {
119 mark::hit!(certain_fuzzy_order_test);
120 let proposed_import_name = match proposed_mod_path.segments.last() {
121 Some(name) => name.to_string().to_lowercase(),
122 None => return usize::MAX,
123 };
124 match proposed_import_name.match_indices(user_input_lowercased).next() {
125 Some((first_matching_index, _)) => first_matching_index,
126 None => usize::MAX,
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use expect_test::{expect, Expect};
133 use test_utils::mark;
134
135 use crate::{
136 item::CompletionKind,
137 test_utils::{check_edit, completion_list},
138 };
139
140 fn check(ra_fixture: &str, expect: Expect) {
141 let actual = completion_list(ra_fixture, CompletionKind::Magic);
142 expect.assert_eq(&actual);
143 }
144
145 #[test]
146 fn function_fuzzy_completion() {
147 check_edit(
148 "stdin",
149 r#"
150//- /lib.rs crate:dep
151pub mod io {
152 pub fn stdin() {}
153};
154
155//- /main.rs crate:main deps:dep
156fn main() {
157 stdi$0
158}
159"#,
160 r#"
161use dep::io::stdin;
162
163fn main() {
164 stdin()$0
165}
166"#,
167 );
168 }
169
170 #[test]
171 fn macro_fuzzy_completion() {
172 check_edit(
173 "macro_with_curlies!",
174 r#"
175//- /lib.rs crate:dep
176/// Please call me as macro_with_curlies! {}
177#[macro_export]
178macro_rules! macro_with_curlies {
179 () => {}
180}
181
182//- /main.rs crate:main deps:dep
183fn main() {
184 curli$0
185}
186"#,
187 r#"
188use dep::macro_with_curlies;
189
190fn main() {
191 macro_with_curlies! {$0}
192}
193"#,
194 );
195 }
196
197 #[test]
198 fn struct_fuzzy_completion() {
199 check_edit(
200 "ThirdStruct",
201 r#"
202//- /lib.rs crate:dep
203pub struct FirstStruct;
204pub mod some_module {
205 pub struct SecondStruct;
206 pub struct ThirdStruct;
207}
208
209//- /main.rs crate:main deps:dep
210use dep::{FirstStruct, some_module::SecondStruct};
211
212fn main() {
213 this$0
214}
215"#,
216 r#"
217use dep::{FirstStruct, some_module::{SecondStruct, ThirdStruct}};
218
219fn main() {
220 ThirdStruct
221}
222"#,
223 );
224 }
225
226 #[test]
227 fn fuzzy_completions_come_in_specific_order() {
228 mark::check!(certain_fuzzy_order_test);
229 check(
230 r#"
231//- /lib.rs crate:dep
232pub struct FirstStruct;
233pub mod some_module {
234 // already imported, omitted
235 pub struct SecondStruct;
236 // does not contain all letters from the query, omitted
237 pub struct UnrelatedOne;
238 // contains all letters from the query, but not in sequence, displayed last
239 pub struct ThiiiiiirdStruct;
240 // contains all letters from the query, but not in the beginning, displayed second
241 pub struct AfterThirdStruct;
242 // contains all letters from the query in the begginning, displayed first
243 pub struct ThirdStruct;
244}
245
246//- /main.rs crate:main deps:dep
247use dep::{FirstStruct, some_module::SecondStruct};
248
249fn main() {
250 hir$0
251}
252"#,
253 expect![[r#"
254 st dep::some_module::ThirdStruct
255 st dep::some_module::AfterThirdStruct
256 st dep::some_module::ThiiiiiirdStruct
257 "#]],
258 );
259 }
260
261 #[test]
262 fn does_not_propose_names_in_scope() {
263 check(
264 r#"
265//- /lib.rs crate:dep
266pub mod test_mod {
267 pub trait TestTrait {
268 const SPECIAL_CONST: u8;
269 type HumbleType;
270 fn weird_function();
271 fn random_method(&self);
272 }
273 pub struct TestStruct {}
274 impl TestTrait for TestStruct {
275 const SPECIAL_CONST: u8 = 42;
276 type HumbleType = ();
277 fn weird_function() {}
278 fn random_method(&self) {}
279 }
280}
281
282//- /main.rs crate:main deps:dep
283use dep::test_mod::TestStruct;
284fn main() {
285 TestSt$0
286}
287"#,
288 expect![[r#""#]],
289 );
290 }
291}
diff --git a/crates/completion/src/completions/keyword.rs b/crates/completion/src/completions/keyword.rs
index c1af348dc..47e146128 100644
--- a/crates/completion/src/completions/keyword.rs
+++ b/crates/completion/src/completions/keyword.rs
@@ -99,7 +99,7 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
99 add_keyword(ctx, acc, "else if", "else if $0 {}"); 99 add_keyword(ctx, acc, "else if", "else if $0 {}");
100 } 100 }
101 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent { 101 if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
102 add_keyword(ctx, acc, "mod", "mod $0 {}"); 102 add_keyword(ctx, acc, "mod", "mod $0");
103 } 103 }
104 if ctx.bind_pat_parent || ctx.ref_pat_parent { 104 if ctx.bind_pat_parent || ctx.ref_pat_parent {
105 add_keyword(ctx, acc, "mut", "mut "); 105 add_keyword(ctx, acc, "mut", "mut ");
diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs
index 53e1391f3..ac5596ca4 100644
--- a/crates/completion/src/completions/unqualified_path.rs
+++ b/crates/completion/src/completions/unqualified_path.rs
@@ -2,17 +2,11 @@
2 2
3use std::iter; 3use std::iter;
4 4
5use either::Either; 5use hir::{Adt, ModuleDef, ScopeDef, Type};
6use hir::{Adt, ModPath, ModuleDef, ScopeDef, Type};
7use ide_db::helpers::insert_use::ImportScope;
8use ide_db::imports_locator;
9use syntax::AstNode; 6use syntax::AstNode;
10use test_utils::mark; 7use test_utils::mark;
11 8
12use crate::{ 9use crate::{CompletionContext, Completions};
13 render::{render_resolution_with_import, RenderContext},
14 CompletionContext, Completions, ImportEdit,
15};
16 10
17pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { 11pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
18 if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) { 12 if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) {
@@ -45,10 +39,6 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
45 } 39 }
46 acc.add_resolution(ctx, name.to_string(), &res) 40 acc.add_resolution(ctx, name.to_string(), &res)
47 }); 41 });
48
49 if ctx.config.enable_autoimport_completions {
50 fuzzy_completion(acc, ctx);
51 }
52} 42}
53 43
54fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) { 44fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) {
@@ -77,124 +67,13 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T
77 } 67 }
78} 68}
79 69
80// Feature: Fuzzy Completion and Autoimports
81//
82// When completing names in the current scope, proposes additional imports from other modules or crates,
83// if they can be qualified in the scope and their name contains all symbols from the completion input
84// (case-insensitive, in any order or places).
85//
86// ```
87// fn main() {
88// pda$0
89// }
90// # pub mod std { pub mod marker { pub struct PhantomData { } } }
91// ```
92// ->
93// ```
94// use std::marker::PhantomData;
95//
96// fn main() {
97// PhantomData
98// }
99// # pub mod std { pub mod marker { pub struct PhantomData { } } }
100// ```
101//
102// .Fuzzy search details
103//
104// To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only
105// (i.e. in `HashMap` in the `std::collections::HashMap` path).
106// For the same reasons, avoids searching for any imports for inputs with their length less that 2 symbols.
107//
108// .Merge Behavior
109//
110// It is possible to configure how use-trees are merged with the `importMergeBehavior` setting.
111// Mimics the corresponding behavior of the `Auto Import` feature.
112//
113// .LSP and performance implications
114//
115// The feature is enabled only if the LSP client supports LSP protocol version 3.16+ and reports the `additionalTextEdits`
116// (case sensitive) resolve client capability in its client capabilities.
117// This way the server is able to defer the costly computations, doing them for a selected completion item only.
118// For clients with no such support, all edits have to be calculated on the completion request, including the fuzzy search completion ones,
119// which might be slow ergo the feature is automatically disabled.
120//
121// .Feature toggle
122//
123// The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.enableAutoimportCompletions` flag.
124// Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding
125// capability enabled.
126fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
127 let potential_import_name = ctx.token.to_string();
128 let _p = profile::span("fuzzy_completion").detail(|| potential_import_name.clone());
129
130 if potential_import_name.len() < 2 {
131 return None;
132 }
133
134 let current_module = ctx.scope.module()?;
135 let anchor = ctx.name_ref_syntax.as_ref()?;
136 let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?;
137
138 let user_input_lowercased = potential_import_name.to_lowercase();
139 let mut all_mod_paths = imports_locator::find_similar_imports(
140 &ctx.sema,
141 ctx.krate?,
142 Some(40),
143 potential_import_name,
144 true,
145 true,
146 )
147 .filter_map(|import_candidate| {
148 Some(match import_candidate {
149 Either::Left(module_def) => {
150 (current_module.find_use_path(ctx.db, module_def)?, ScopeDef::ModuleDef(module_def))
151 }
152 Either::Right(macro_def) => {
153 (current_module.find_use_path(ctx.db, macro_def)?, ScopeDef::MacroDef(macro_def))
154 }
155 })
156 })
157 .filter(|(mod_path, _)| mod_path.len() > 1)
158 .collect::<Vec<_>>();
159
160 all_mod_paths.sort_by_cached_key(|(mod_path, _)| {
161 compute_fuzzy_completion_order_key(mod_path, &user_input_lowercased)
162 });
163
164 acc.add_all(all_mod_paths.into_iter().filter_map(|(import_path, definition)| {
165 render_resolution_with_import(
166 RenderContext::new(ctx),
167 ImportEdit { import_path, import_scope: import_scope.clone() },
168 &definition,
169 )
170 }));
171 Some(())
172}
173
174fn compute_fuzzy_completion_order_key(
175 proposed_mod_path: &ModPath,
176 user_input_lowercased: &str,
177) -> usize {
178 mark::hit!(certain_fuzzy_order_test);
179 let proposed_import_name = match proposed_mod_path.segments.last() {
180 Some(name) => name.to_string().to_lowercase(),
181 None => return usize::MAX,
182 };
183 match proposed_import_name.match_indices(user_input_lowercased).next() {
184 Some((first_matching_index, _)) => first_matching_index,
185 None => usize::MAX,
186 }
187}
188
189#[cfg(test)] 70#[cfg(test)]
190mod tests { 71mod tests {
191 use expect_test::{expect, Expect}; 72 use expect_test::{expect, Expect};
192 use test_utils::mark; 73 use test_utils::mark;
193 74
194 use crate::{ 75 use crate::{
195 test_utils::{ 76 test_utils::{check_edit, completion_list_with_config, TEST_CONFIG},
196 check_edit, check_edit_with_config, completion_list_with_config, TEST_CONFIG,
197 },
198 CompletionConfig, CompletionKind, 77 CompletionConfig, CompletionKind,
199 }; 78 };
200 79
@@ -855,128 +734,4 @@ impl My$0
855 "#]], 734 "#]],
856 ) 735 )
857 } 736 }
858
859 #[test]
860 fn function_fuzzy_completion() {
861 check_edit_with_config(
862 TEST_CONFIG,
863 "stdin",
864 r#"
865//- /lib.rs crate:dep
866pub mod io {
867 pub fn stdin() {}
868};
869
870//- /main.rs crate:main deps:dep
871fn main() {
872 stdi$0
873}
874"#,
875 r#"
876use dep::io::stdin;
877
878fn main() {
879 stdin()$0
880}
881"#,
882 );
883 }
884
885 #[test]
886 fn macro_fuzzy_completion() {
887 check_edit_with_config(
888 TEST_CONFIG,
889 "macro_with_curlies!",
890 r#"
891//- /lib.rs crate:dep
892/// Please call me as macro_with_curlies! {}
893#[macro_export]
894macro_rules! macro_with_curlies {
895 () => {}
896}
897
898//- /main.rs crate:main deps:dep
899fn main() {
900 curli$0
901}
902"#,
903 r#"
904use dep::macro_with_curlies;
905
906fn main() {
907 macro_with_curlies! {$0}
908}
909"#,
910 );
911 }
912
913 #[test]
914 fn struct_fuzzy_completion() {
915 check_edit_with_config(
916 TEST_CONFIG,
917 "ThirdStruct",
918 r#"
919//- /lib.rs crate:dep
920pub struct FirstStruct;
921pub mod some_module {
922 pub struct SecondStruct;
923 pub struct ThirdStruct;
924}
925
926//- /main.rs crate:main deps:dep
927use dep::{FirstStruct, some_module::SecondStruct};
928
929fn main() {
930 this$0
931}
932"#,
933 r#"
934use dep::{FirstStruct, some_module::{SecondStruct, ThirdStruct}};
935
936fn main() {
937 ThirdStruct
938}
939"#,
940 );
941 }
942
943 #[test]
944 fn fuzzy_completions_come_in_specific_order() {
945 mark::check!(certain_fuzzy_order_test);
946 check_with_config(
947 TEST_CONFIG,
948 r#"
949//- /lib.rs crate:dep
950pub struct FirstStruct;
951pub mod some_module {
952 // already imported, omitted
953 pub struct SecondStruct;
954 // does not contain all letters from the query, omitted
955 pub struct UnrelatedOne;
956 // contains all letters from the query, but not in sequence, displayed last
957 pub struct ThiiiiiirdStruct;
958 // contains all letters from the query, but not in the beginning, displayed second
959 pub struct AfterThirdStruct;
960 // contains all letters from the query in the begginning, displayed first
961 pub struct ThirdStruct;
962}
963
964//- /main.rs crate:main deps:dep
965use dep::{FirstStruct, some_module::SecondStruct};
966
967fn main() {
968 hir$0
969}
970"#,
971 expect![[r#"
972 fn main() fn main()
973 st SecondStruct
974 st FirstStruct
975 md dep
976 st dep::some_module::ThirdStruct
977 st dep::some_module::AfterThirdStruct
978 st dep::some_module::ThiiiiiirdStruct
979 "#]],
980 );
981 }
982} 737}
diff --git a/crates/completion/src/config.rs b/crates/completion/src/config.rs
index b4439b7d1..58fc700f3 100644
--- a/crates/completion/src/config.rs
+++ b/crates/completion/src/config.rs
@@ -4,7 +4,7 @@
4//! module, and we use to statically check that we only produce snippet 4//! module, and we use to statically check that we only produce snippet
5//! completions if we are allowed to. 5//! completions if we are allowed to.
6 6
7use ide_db::helpers::{insert_use::MergeBehavior, SnippetCap}; 7use ide_db::helpers::{insert_use::InsertUseConfig, SnippetCap};
8 8
9#[derive(Clone, Debug, PartialEq, Eq)] 9#[derive(Clone, Debug, PartialEq, Eq)]
10pub struct CompletionConfig { 10pub struct CompletionConfig {
@@ -13,5 +13,5 @@ pub struct CompletionConfig {
13 pub add_call_parenthesis: bool, 13 pub add_call_parenthesis: bool,
14 pub add_call_argument_snippets: bool, 14 pub add_call_argument_snippets: bool,
15 pub snippet_cap: Option<SnippetCap>, 15 pub snippet_cap: Option<SnippetCap>,
16 pub merge: Option<MergeBehavior>, 16 pub insert_use: InsertUseConfig,
17} 17}
diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs
index 0134ff219..5d91d3a5c 100644
--- a/crates/completion/src/item.rs
+++ b/crates/completion/src/item.rs
@@ -398,7 +398,9 @@ impl Builder {
398 pub(crate) fn set_detail(mut self, detail: Option<impl Into<String>>) -> Builder { 398 pub(crate) fn set_detail(mut self, detail: Option<impl Into<String>>) -> Builder {
399 self.detail = detail.map(Into::into); 399 self.detail = detail.map(Into::into);
400 if let Some(detail) = &self.detail { 400 if let Some(detail) = &self.detail {
401 assert_never!(detail.contains('\n'), "multiline detail: {}", detail); 401 if assert_never!(detail.contains('\n'), "multiline detail: {}", detail) {
402 self.detail = Some(detail.splitn(2, '\n').next().unwrap().to_string());
403 }
402 } 404 }
403 self 405 self
404 } 406 }
diff --git a/crates/completion/src/lib.rs b/crates/completion/src/lib.rs
index 6cba88a6b..ee1b822e7 100644
--- a/crates/completion/src/lib.rs
+++ b/crates/completion/src/lib.rs
@@ -127,6 +127,7 @@ pub fn completions(
127 completions::macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx); 127 completions::macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx);
128 completions::trait_impl::complete_trait_impl(&mut acc, &ctx); 128 completions::trait_impl::complete_trait_impl(&mut acc, &ctx);
129 completions::mod_::complete_mod(&mut acc, &ctx); 129 completions::mod_::complete_mod(&mut acc, &ctx);
130 completions::flyimport::import_on_the_fly(&mut acc, &ctx);
130 131
131 Some(acc) 132 Some(acc)
132} 133}
@@ -153,7 +154,9 @@ pub fn resolve_completion_edits(
153 }) 154 })
154 .find(|mod_path| mod_path.to_string() == full_import_path)?; 155 .find(|mod_path| mod_path.to_string() == full_import_path)?;
155 156
156 ImportEdit { import_path, import_scope }.to_text_edit(config.merge).map(|edit| vec![edit]) 157 ImportEdit { import_path, import_scope }
158 .to_text_edit(config.insert_use.merge)
159 .map(|edit| vec![edit])
157} 160}
158 161
159#[cfg(test)] 162#[cfg(test)]
diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs
index e93c59f71..820dd01d1 100644
--- a/crates/completion/src/render.rs
+++ b/crates/completion/src/render.rs
@@ -51,11 +51,16 @@ pub(crate) fn render_resolution_with_import<'a>(
51 import_edit: ImportEdit, 51 import_edit: ImportEdit,
52 resolution: &ScopeDef, 52 resolution: &ScopeDef,
53) -> Option<CompletionItem> { 53) -> Option<CompletionItem> {
54 Render::new(ctx).render_resolution( 54 Render::new(ctx)
55 import_edit.import_path.segments.last()?.to_string(), 55 .render_resolution(
56 Some(import_edit), 56 import_edit.import_path.segments.last()?.to_string(),
57 resolution, 57 Some(import_edit),
58 ) 58 resolution,
59 )
60 .map(|mut item| {
61 item.completion_kind = CompletionKind::Magic;
62 item
63 })
59} 64}
60 65
61/// Interface for data and methods required for items rendering. 66/// Interface for data and methods required for items rendering.
diff --git a/crates/completion/src/test_utils.rs b/crates/completion/src/test_utils.rs
index 3f4b9d4ac..6ea6da989 100644
--- a/crates/completion/src/test_utils.rs
+++ b/crates/completion/src/test_utils.rs
@@ -1,9 +1,12 @@
1//! Runs completion for testing purposes. 1//! Runs completion for testing purposes.
2 2
3use hir::Semantics; 3use hir::{PrefixKind, Semantics};
4use ide_db::{ 4use ide_db::{
5 base_db::{fixture::ChangeFixture, FileLoader, FilePosition}, 5 base_db::{fixture::ChangeFixture, FileLoader, FilePosition},
6 helpers::{insert_use::MergeBehavior, SnippetCap}, 6 helpers::{
7 insert_use::{InsertUseConfig, MergeBehavior},
8 SnippetCap,
9 },
7 RootDatabase, 10 RootDatabase,
8}; 11};
9use itertools::Itertools; 12use itertools::Itertools;
@@ -19,7 +22,10 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig {
19 add_call_parenthesis: true, 22 add_call_parenthesis: true,
20 add_call_argument_snippets: true, 23 add_call_argument_snippets: true,
21 snippet_cap: SnippetCap::new(true), 24 snippet_cap: SnippetCap::new(true),
22 merge: Some(MergeBehavior::Full), 25 insert_use: InsertUseConfig {
26 merge: Some(MergeBehavior::Full),
27 prefix_kind: PrefixKind::Plain,
28 },
23}; 29};
24 30
25/// Creates analysis from a multi-file fixture, returns positions marked with $0. 31/// Creates analysis from a multi-file fixture, returns positions marked with $0.
@@ -110,7 +116,7 @@ pub(crate) fn check_edit_with_config(
110 116
111 let mut combined_edit = completion.text_edit().to_owned(); 117 let mut combined_edit = completion.text_edit().to_owned();
112 if let Some(import_text_edit) = 118 if let Some(import_text_edit) =
113 completion.import_to_add().and_then(|edit| edit.to_text_edit(config.merge)) 119 completion.import_to_add().and_then(|edit| edit.to_text_edit(config.insert_use.merge))
114 { 120 {
115 combined_edit.union(import_text_edit).expect( 121 combined_edit.union(import_text_edit).expect(
116 "Failed to apply completion resolve changes: change ranges overlap, but should not", 122 "Failed to apply completion resolve changes: change ranges overlap, but should not",
diff --git a/crates/hir_def/Cargo.toml b/crates/hir_def/Cargo.toml
index 5d21283f7..535221294 100644
--- a/crates/hir_def/Cargo.toml
+++ b/crates/hir_def/Cargo.toml
@@ -20,7 +20,7 @@ fst = { version = "0.4", default-features = false }
20itertools = "0.10.0" 20itertools = "0.10.0"
21indexmap = "1.4.0" 21indexmap = "1.4.0"
22smallvec = "1.4.0" 22smallvec = "1.4.0"
23la-arena = "0.1.0" 23la-arena = { version = "0.2.0", path = "../../lib/arena" }
24 24
25stdx = { path = "../stdx", version = "0.0.0" } 25stdx = { path = "../stdx", version = "0.0.0" }
26base_db = { path = "../base_db", version = "0.0.0" } 26base_db = { path = "../base_db", version = "0.0.0" }
diff --git a/crates/hir_def/src/adt.rs b/crates/hir_def/src/adt.rs
index 237c3d3f9..06f0b9b18 100644
--- a/crates/hir_def/src/adt.rs
+++ b/crates/hir_def/src/adt.rs
@@ -8,7 +8,7 @@ use hir_expand::{
8 name::{AsName, Name}, 8 name::{AsName, Name},
9 InFile, 9 InFile,
10}; 10};
11use la_arena::{map::ArenaMap, Arena}; 11use la_arena::{Arena, ArenaMap};
12use syntax::ast::{self, NameOwner, VisibilityOwner}; 12use syntax::ast::{self, NameOwner, VisibilityOwner};
13use tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree}; 13use tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree};
14 14
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index 5a86823c2..1b09ff816 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -7,7 +7,7 @@ use cfg::{CfgExpr, CfgOptions};
7use either::Either; 7use either::Either;
8use hir_expand::{hygiene::Hygiene, name::AsName, AstId, InFile}; 8use hir_expand::{hygiene::Hygiene, name::AsName, AstId, InFile};
9use itertools::Itertools; 9use itertools::Itertools;
10use la_arena::map::ArenaMap; 10use la_arena::ArenaMap;
11use mbe::ast_to_token_tree; 11use mbe::ast_to_token_tree;
12use syntax::{ 12use syntax::{
13 ast::{self, AstNode, AttrsOwner}, 13 ast::{self, AstNode, AttrsOwner},
diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs
index 43ee57277..344f0b6c0 100644
--- a/crates/hir_def/src/body.rs
+++ b/crates/hir_def/src/body.rs
@@ -16,7 +16,7 @@ use hir_expand::{
16 ast_id_map::AstIdMap, diagnostics::DiagnosticSink, hygiene::Hygiene, AstId, ExpandResult, 16 ast_id_map::AstIdMap, diagnostics::DiagnosticSink, hygiene::Hygiene, AstId, ExpandResult,
17 HirFileId, InFile, MacroDefId, 17 HirFileId, InFile, MacroDefId,
18}; 18};
19use la_arena::{map::ArenaMap, Arena}; 19use la_arena::{Arena, ArenaMap};
20use rustc_hash::FxHashMap; 20use rustc_hash::FxHashMap;
21use syntax::{ast, AstNode, AstPtr}; 21use syntax::{ast, AstNode, AstPtr};
22use test_utils::mark; 22use test_utils::mark;
diff --git a/crates/hir_def/src/db.rs b/crates/hir_def/src/db.rs
index 0506a7274..6ef9fe790 100644
--- a/crates/hir_def/src/db.rs
+++ b/crates/hir_def/src/db.rs
@@ -3,7 +3,7 @@ use std::sync::Arc;
3 3
4use base_db::{salsa, CrateId, SourceDatabase, Upcast}; 4use base_db::{salsa, CrateId, SourceDatabase, Upcast};
5use hir_expand::{db::AstDatabase, HirFileId}; 5use hir_expand::{db::AstDatabase, HirFileId};
6use la_arena::map::ArenaMap; 6use la_arena::ArenaMap;
7use syntax::SmolStr; 7use syntax::SmolStr;
8 8
9use crate::{ 9use crate::{
diff --git a/crates/hir_def/src/expr.rs b/crates/hir_def/src/expr.rs
index a293df9f1..5be838f4a 100644
--- a/crates/hir_def/src/expr.rs
+++ b/crates/hir_def/src/expr.rs
@@ -13,7 +13,7 @@
13//! See also a neighboring `body` module. 13//! See also a neighboring `body` module.
14 14
15use hir_expand::name::Name; 15use hir_expand::name::Name;
16use la_arena::{Idx, RawId}; 16use la_arena::{Idx, RawIdx};
17use syntax::ast::RangeOp; 17use syntax::ast::RangeOp;
18 18
19use crate::{ 19use crate::{
@@ -24,7 +24,7 @@ use crate::{
24 24
25pub type ExprId = Idx<Expr>; 25pub type ExprId = Idx<Expr>;
26pub(crate) fn dummy_expr_id() -> ExprId { 26pub(crate) fn dummy_expr_id() -> ExprId {
27 ExprId::from_raw(RawId::from(!0)) 27 ExprId::from_raw(RawIdx::from(!0))
28} 28}
29 29
30pub type PatId = Idx<Pat>; 30pub type PatId = Idx<Pat>;
diff --git a/crates/hir_def/src/generics.rs b/crates/hir_def/src/generics.rs
index 75eab791a..3ace3be1f 100644
--- a/crates/hir_def/src/generics.rs
+++ b/crates/hir_def/src/generics.rs
@@ -10,7 +10,7 @@ use hir_expand::{
10 name::{name, AsName, Name}, 10 name::{name, AsName, Name},
11 InFile, 11 InFile,
12}; 12};
13use la_arena::{map::ArenaMap, Arena}; 13use la_arena::{Arena, ArenaMap};
14use syntax::ast::{self, GenericParamsOwner, NameOwner, TypeBoundsOwner}; 14use syntax::ast::{self, GenericParamsOwner, NameOwner, TypeBoundsOwner};
15 15
16use crate::{ 16use crate::{
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index 91e42aa0d..9a433b61c 100644
--- a/crates/hir_def/src/item_tree.rs
+++ b/crates/hir_def/src/item_tree.rs
@@ -20,7 +20,7 @@ use hir_expand::{
20 name::{name, AsName, Name}, 20 name::{name, AsName, Name},
21 HirFileId, InFile, 21 HirFileId, InFile,
22}; 22};
23use la_arena::{Arena, Idx, RawId}; 23use la_arena::{Arena, Idx, RawIdx};
24use rustc_hash::FxHashMap; 24use rustc_hash::FxHashMap;
25use smallvec::SmallVec; 25use smallvec::SmallVec;
26use syntax::{ast, match_ast}; 26use syntax::{ast, match_ast};
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs
index 3b206ef85..5e71ca42c 100644
--- a/crates/hir_def/src/item_tree/lower.rs
+++ b/crates/hir_def/src/item_tree/lower.rs
@@ -683,12 +683,12 @@ impl Ctx {
683 } 683 }
684 684
685 fn next_field_idx(&self) -> Idx<Field> { 685 fn next_field_idx(&self) -> Idx<Field> {
686 Idx::from_raw(RawId::from( 686 Idx::from_raw(RawIdx::from(
687 self.tree.data.as_ref().map_or(0, |data| data.fields.len() as u32), 687 self.tree.data.as_ref().map_or(0, |data| data.fields.len() as u32),
688 )) 688 ))
689 } 689 }
690 fn next_variant_idx(&self) -> Idx<Variant> { 690 fn next_variant_idx(&self) -> Idx<Variant> {
691 Idx::from_raw(RawId::from( 691 Idx::from_raw(RawIdx::from(
692 self.tree.data.as_ref().map_or(0, |data| data.variants.len() as u32), 692 self.tree.data.as_ref().map_or(0, |data| data.variants.len() as u32),
693 )) 693 ))
694 } 694 }
diff --git a/crates/hir_def/src/src.rs b/crates/hir_def/src/src.rs
index eb29265d9..751d4c052 100644
--- a/crates/hir_def/src/src.rs
+++ b/crates/hir_def/src/src.rs
@@ -1,7 +1,7 @@
1//! Utilities for mapping between hir IDs and the surface syntax. 1//! Utilities for mapping between hir IDs and the surface syntax.
2 2
3use hir_expand::InFile; 3use hir_expand::InFile;
4use la_arena::map::ArenaMap; 4use la_arena::ArenaMap;
5 5
6use crate::{db::DefDatabase, item_tree::ItemTreeNode, AssocItemLoc, ItemLoc}; 6use crate::{db::DefDatabase, item_tree::ItemTreeNode, AssocItemLoc, ItemLoc};
7 7
diff --git a/crates/hir_def/src/trace.rs b/crates/hir_def/src/trace.rs
index 0a9beae8e..6e6ceb8e4 100644
--- a/crates/hir_def/src/trace.rs
+++ b/crates/hir_def/src/trace.rs
@@ -9,7 +9,7 @@
9//! absolute offsets. The `Trace` structure (inspired, at least in name, by 9//! absolute offsets. The `Trace` structure (inspired, at least in name, by
10//! Kotlin's `BindingTrace`) allows use the same code to compute both 10//! Kotlin's `BindingTrace`) allows use the same code to compute both
11//! projections. 11//! projections.
12use la_arena::{map::ArenaMap, Arena, Idx, RawId}; 12use la_arena::{Arena, ArenaMap, Idx, RawIdx};
13 13
14pub(crate) struct Trace<T, V> { 14pub(crate) struct Trace<T, V> {
15 arena: Option<Arena<T>>, 15 arena: Option<Arena<T>>,
@@ -30,7 +30,7 @@ impl<T, V> Trace<T, V> {
30 let id = if let Some(arena) = &mut self.arena { 30 let id = if let Some(arena) = &mut self.arena {
31 arena.alloc(data()) 31 arena.alloc(data())
32 } else { 32 } else {
33 let id = Idx::<T>::from_raw(RawId::from(self.len)); 33 let id = Idx::<T>::from_raw(RawIdx::from(self.len));
34 self.len += 1; 34 self.len += 1;
35 id 35 id
36 }; 36 };
diff --git a/crates/hir_expand/Cargo.toml b/crates/hir_expand/Cargo.toml
index b535a3d4f..5271110d2 100644
--- a/crates/hir_expand/Cargo.toml
+++ b/crates/hir_expand/Cargo.toml
@@ -13,7 +13,7 @@ doctest = false
13log = "0.4.8" 13log = "0.4.8"
14either = "1.5.3" 14either = "1.5.3"
15rustc-hash = "1.0.0" 15rustc-hash = "1.0.0"
16la-arena = "0.1.0" 16la-arena = { version = "0.2.0", path = "../../lib/arena" }
17 17
18base_db = { path = "../base_db", version = "0.0.0" } 18base_db = { path = "../base_db", version = "0.0.0" }
19syntax = { path = "../syntax", version = "0.0.0" } 19syntax = { path = "../syntax", version = "0.0.0" }
diff --git a/crates/hir_expand/src/ast_id_map.rs b/crates/hir_expand/src/ast_id_map.rs
index f4f6e11fd..2401b0cc5 100644
--- a/crates/hir_expand/src/ast_id_map.rs
+++ b/crates/hir_expand/src/ast_id_map.rs
@@ -72,10 +72,12 @@ impl AstIdMap {
72 // get lower ids then children. That is, adding a new child does not 72 // get lower ids then children. That is, adding a new child does not
73 // change parent's id. This means that, say, adding a new function to a 73 // change parent's id. This means that, say, adding a new function to a
74 // trait does not change ids of top-level items, which helps caching. 74 // trait does not change ids of top-level items, which helps caching.
75 bfs(node, |it| { 75 bdfs(node, |it| match ast::Item::cast(it) {
76 if let Some(module_item) = ast::Item::cast(it) { 76 Some(module_item) => {
77 res.alloc(module_item.syntax()); 77 res.alloc(module_item.syntax());
78 true
78 } 79 }
80 None => false,
79 }); 81 });
80 res 82 res
81 } 83 }
@@ -105,14 +107,30 @@ impl AstIdMap {
105 } 107 }
106} 108}
107 109
108/// Walks the subtree in bfs order, calling `f` for each node. 110/// Walks the subtree in bdfs order, calling `f` for each node. What is bdfs
109fn bfs(node: &SyntaxNode, mut f: impl FnMut(SyntaxNode)) { 111/// order? It is a mix of breadth-first and depth first orders. Nodes for which
112/// `f` returns true are visited breadth-first, all the other nodes are explored
113/// depth-first.
114///
115/// In other words, the size of the bfs queue is bound by the number of "true"
116/// nodes.
117fn bdfs(node: &SyntaxNode, mut f: impl FnMut(SyntaxNode) -> bool) {
110 let mut curr_layer = vec![node.clone()]; 118 let mut curr_layer = vec![node.clone()];
111 let mut next_layer = vec![]; 119 let mut next_layer = vec![];
112 while !curr_layer.is_empty() { 120 while !curr_layer.is_empty() {
113 curr_layer.drain(..).for_each(|node| { 121 curr_layer.drain(..).for_each(|node| {
114 next_layer.extend(node.children()); 122 let mut preorder = node.preorder();
115 f(node); 123 while let Some(event) = preorder.next() {
124 match event {
125 syntax::WalkEvent::Enter(node) => {
126 if f(node.clone()) {
127 next_layer.extend(node.children());
128 preorder.skip_subtree();
129 }
130 }
131 syntax::WalkEvent::Leave(_) => {}
132 }
133 }
116 }); 134 });
117 std::mem::swap(&mut curr_layer, &mut next_layer); 135 std::mem::swap(&mut curr_layer, &mut next_layer);
118 } 136 }
diff --git a/crates/hir_expand/src/proc_macro.rs b/crates/hir_expand/src/proc_macro.rs
index 1923daca5..75e950816 100644
--- a/crates/hir_expand/src/proc_macro.rs
+++ b/crates/hir_expand/src/proc_macro.rs
@@ -135,7 +135,6 @@ mod tests {
135 let result = format!("{:#?}", remove_derive_attrs(&tt).unwrap()); 135 let result = format!("{:#?}", remove_derive_attrs(&tt).unwrap());
136 136
137 assert_eq_text!( 137 assert_eq_text!(
138 &result,
139 r#" 138 r#"
140SUBTREE $ 139SUBTREE $
141 PUNCH # [alone] 0 140 PUNCH # [alone] 0
@@ -150,7 +149,8 @@ SUBTREE $
150 PUNCH : [alone] 19 149 PUNCH : [alone] 19
151 IDENT u32 20 150 IDENT u32 20
152"# 151"#
153 .trim() 152 .trim(),
153 &result
154 ); 154 );
155 } 155 }
156} 156}
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml
index 436c1405b..98434b741 100644
--- a/crates/hir_ty/Cargo.toml
+++ b/crates/hir_ty/Cargo.toml
@@ -20,7 +20,7 @@ scoped-tls = "1"
20chalk-solve = { version = "0.47", default-features = false } 20chalk-solve = { version = "0.47", default-features = false }
21chalk-ir = "0.47" 21chalk-ir = "0.47"
22chalk-recursive = "0.47" 22chalk-recursive = "0.47"
23la-arena = "0.1.0" 23la-arena = { version = "0.2.0", path = "../../lib/arena" }
24 24
25stdx = { path = "../stdx", version = "0.0.0" } 25stdx = { path = "../stdx", version = "0.0.0" }
26hir_def = { path = "../hir_def", version = "0.0.0" } 26hir_def = { path = "../hir_def", version = "0.0.0" }
diff --git a/crates/hir_ty/src/db.rs b/crates/hir_ty/src/db.rs
index b0e2a3b7d..b3af82444 100644
--- a/crates/hir_ty/src/db.rs
+++ b/crates/hir_ty/src/db.rs
@@ -7,7 +7,7 @@ use hir_def::{
7 db::DefDatabase, expr::ExprId, ConstParamId, DefWithBodyId, FunctionId, GenericDefId, ImplId, 7 db::DefDatabase, expr::ExprId, ConstParamId, DefWithBodyId, FunctionId, GenericDefId, ImplId,
8 LocalFieldId, TypeParamId, VariantId, 8 LocalFieldId, TypeParamId, VariantId,
9}; 9};
10use la_arena::map::ArenaMap; 10use la_arena::ArenaMap;
11 11
12use crate::{ 12use crate::{
13 method_resolution::{InherentImpls, TraitImpls}, 13 method_resolution::{InherentImpls, TraitImpls},
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs
index 46a806b9a..d08867c70 100644
--- a/crates/hir_ty/src/infer.rs
+++ b/crates/hir_ty/src/infer.rs
@@ -30,7 +30,7 @@ use hir_def::{
30 TypeAliasId, VariantId, 30 TypeAliasId, VariantId,
31}; 31};
32use hir_expand::{diagnostics::DiagnosticSink, name::name}; 32use hir_expand::{diagnostics::DiagnosticSink, name::name};
33use la_arena::map::ArenaMap; 33use la_arena::ArenaMap;
34use rustc_hash::FxHashMap; 34use rustc_hash::FxHashMap;
35use stdx::impl_from; 35use stdx::impl_from;
36use syntax::SmolStr; 36use syntax::SmolStr;
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index 68d16f89a..7a734c8b9 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -20,7 +20,7 @@ use hir_def::{
20 TypeAliasId, TypeParamId, UnionId, VariantId, 20 TypeAliasId, TypeParamId, UnionId, VariantId,
21}; 21};
22use hir_expand::name::Name; 22use hir_expand::name::Name;
23use la_arena::map::ArenaMap; 23use la_arena::ArenaMap;
24use smallvec::SmallVec; 24use smallvec::SmallVec;
25use stdx::impl_from; 25use stdx::impl_from;
26use test_utils::mark; 26use test_utils::mark;
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index afd552008..6c94c26b5 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -80,24 +80,21 @@ pub use crate::{
80 HlRange, 80 HlRange,
81 }, 81 },
82}; 82};
83pub use assists::{Assist, AssistConfig, AssistId, AssistKind, InsertUseConfig}; 83pub use assists::{Assist, AssistConfig, AssistId, AssistKind};
84pub use completion::{ 84pub use completion::{
85 CompletionConfig, CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, 85 CompletionConfig, CompletionItem, CompletionItemKind, CompletionScore, ImportEdit,
86 InsertTextFormat, 86 InsertTextFormat,
87}; 87};
88pub use hir::{Documentation, Semantics}; 88pub use hir::{Documentation, Semantics};
89pub use ide_db::base_db::{
90 Canceled, Change, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRoot,
91 SourceRootId,
92};
93pub use ide_db::{ 89pub use ide_db::{
90 base_db::{
91 Canceled, Change, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange,
92 SourceRoot, SourceRootId,
93 },
94 call_info::CallInfo, 94 call_info::CallInfo,
95 search::{FileReference, ReferenceAccess, ReferenceKind},
96};
97pub use ide_db::{
98 label::Label, 95 label::Label,
99 line_index::{LineCol, LineIndex}, 96 line_index::{LineCol, LineIndex},
100 search::SearchScope, 97 search::{FileReference, ReferenceAccess, ReferenceKind, SearchScope},
101 source_change::{FileSystemEdit, SourceChange}, 98 source_change::{FileSystemEdit, SourceChange},
102 symbol_index::Query, 99 symbol_index::Query,
103 RootDatabase, 100 RootDatabase,
diff --git a/crates/ide/src/syntax_tree.rs b/crates/ide/src/syntax_tree.rs
index 1d4bac7ad..4c63d3023 100644
--- a/crates/ide/src/syntax_tree.rs
+++ b/crates/ide/src/syntax_tree.rs
@@ -111,7 +111,6 @@ mod tests {
111 let syn = analysis.syntax_tree(file_id, None).unwrap(); 111 let syn = analysis.syntax_tree(file_id, None).unwrap();
112 112
113 assert_eq_text!( 113 assert_eq_text!(
114 syn.trim(),
115 r#" 114 r#"
116[email protected] 115[email protected]
117 [email protected] 116 [email protected]
@@ -127,7 +126,8 @@ [email protected]
127 [email protected] "{" 126 [email protected] "{"
128 [email protected] "}" 127 [email protected] "}"
129"# 128"#
130 .trim() 129 .trim(),
130 syn.trim()
131 ); 131 );
132 132
133 let (analysis, file_id) = fixture::file( 133 let (analysis, file_id) = fixture::file(
@@ -143,7 +143,6 @@ fn test() {
143 let syn = analysis.syntax_tree(file_id, None).unwrap(); 143 let syn = analysis.syntax_tree(file_id, None).unwrap();
144 144
145 assert_eq_text!( 145 assert_eq_text!(
146 syn.trim(),
147 r#" 146 r#"
148[email protected] 147[email protected]
149 [email protected] 148 [email protected]
@@ -176,7 +175,8 @@ [email protected]
176 [email protected] "\n" 175 [email protected] "\n"
177 [email protected] "}" 176 [email protected] "}"
178"# 177"#
179 .trim() 178 .trim(),
179 syn.trim()
180 ); 180 );
181 } 181 }
182 182
@@ -186,7 +186,6 @@ [email protected]
186 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); 186 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap();
187 187
188 assert_eq_text!( 188 assert_eq_text!(
189 syn.trim(),
190 r#" 189 r#"
191[email protected] 190[email protected]
192 [email protected] "fn" 191 [email protected] "fn"
@@ -201,7 +200,8 @@ [email protected]
201 [email protected] "{" 200 [email protected] "{"
202 [email protected] "}" 201 [email protected] "}"
203"# 202"#
204 .trim() 203 .trim(),
204 syn.trim()
205 ); 205 );
206 206
207 let (analysis, range) = fixture::range( 207 let (analysis, range) = fixture::range(
@@ -216,7 +216,6 @@ [email protected]
216 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); 216 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap();
217 217
218 assert_eq_text!( 218 assert_eq_text!(
219 syn.trim(),
220 r#" 219 r#"
221[email protected] 220[email protected]
222 [email protected] 221 [email protected]
@@ -234,7 +233,8 @@ [email protected]
234 [email protected] ")" 233 [email protected] ")"
235 [email protected] ";" 234 [email protected] ";"
236"# 235"#
237 .trim() 236 .trim(),
237 syn.trim()
238 ); 238 );
239 } 239 }
240 240
@@ -253,7 +253,6 @@ fn bar() {
253 ); 253 );
254 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); 254 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap();
255 assert_eq_text!( 255 assert_eq_text!(
256 syn.trim(),
257 r#" 256 r#"
258[email protected] 257[email protected]
259 [email protected] 258 [email protected]
@@ -270,7 +269,8 @@ [email protected]
270 [email protected] "\n" 269 [email protected] "\n"
271 [email protected] "}" 270 [email protected] "}"
272"# 271"#
273 .trim() 272 .trim(),
273 syn.trim()
274 ); 274 );
275 275
276 // With a raw string 276 // With a raw string
@@ -287,7 +287,6 @@ fn bar() {
287 ); 287 );
288 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); 288 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap();
289 assert_eq_text!( 289 assert_eq_text!(
290 syn.trim(),
291 r#" 290 r#"
292[email protected] 291[email protected]
293 [email protected] 292 [email protected]
@@ -304,7 +303,8 @@ [email protected]
304 [email protected] "\n" 303 [email protected] "\n"
305 [email protected] "}" 304 [email protected] "}"
306"# 305"#
307 .trim() 306 .trim(),
307 syn.trim()
308 ); 308 );
309 309
310 // With a raw string 310 // With a raw string
@@ -320,7 +320,6 @@ fn bar() {
320 ); 320 );
321 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); 321 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap();
322 assert_eq_text!( 322 assert_eq_text!(
323 syn.trim(),
324 r#" 323 r#"
325[email protected] 324[email protected]
326 [email protected] 325 [email protected]
@@ -351,7 +350,8 @@ [email protected]
351 [email protected] "\n" 350 [email protected] "\n"
352 [email protected] "}" 351 [email protected] "}"
353"# 352"#
354 .trim() 353 .trim(),
354 syn.trim()
355 ); 355 );
356 } 356 }
357} 357}
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs
index c6763ae36..0dcc4dd29 100644
--- a/crates/ide_db/src/helpers.rs
+++ b/crates/ide_db/src/helpers.rs
@@ -1,5 +1,6 @@
1//! A module with ide helpers for high-level ide features. 1//! A module with ide helpers for high-level ide features.
2pub mod insert_use; 2pub mod insert_use;
3pub mod import_assets;
3 4
4use hir::{Crate, Enum, Module, ScopeDef, Semantics, Trait}; 5use hir::{Crate, Enum, Module, ScopeDef, Semantics, Trait};
5use syntax::ast::{self, make}; 6use syntax::ast::{self, make};
diff --git a/crates/assists/src/utils/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs
index 4ce82c1ba..edc3da318 100644
--- a/crates/assists/src/utils/import_assets.rs
+++ b/crates/ide_db/src/helpers/import_assets.rs
@@ -1,19 +1,17 @@
1//! Look up accessible paths for items. 1//! Look up accessible paths for items.
2use either::Either; 2use either::Either;
3use hir::{AsAssocItem, AssocItemContainer, ModuleDef, Semantics}; 3use hir::{AsAssocItem, AssocItemContainer, ModuleDef, Semantics};
4use ide_db::{imports_locator, RootDatabase};
5use rustc_hash::FxHashSet; 4use rustc_hash::FxHashSet;
6use syntax::{ast, AstNode, SyntaxNode}; 5use syntax::{ast, AstNode, SyntaxNode};
7 6
8use crate::assist_config::InsertUseConfig; 7use crate::{imports_locator, RootDatabase};
8
9use super::insert_use::InsertUseConfig;
9 10
10#[derive(Debug)] 11#[derive(Debug)]
11pub(crate) enum ImportCandidate { 12pub enum ImportCandidate {
12 /// Simple name like 'HashMap' 13 // A path, qualified (`std::collections::HashMap`) or not (`HashMap`).
13 UnqualifiedName(PathImportCandidate), 14 Path(PathImportCandidate),
14 /// First part of the qualified name.
15 /// For 'std::collections::HashMap', that will be 'std'.
16 QualifierStart(PathImportCandidate),
17 /// A trait associated function (with no self parameter) or associated constant. 15 /// A trait associated function (with no self parameter) or associated constant.
18 /// For 'test_mod::TestEnum::test_function', `ty` is the `test_mod::TestEnum` expression type 16 /// For 'test_mod::TestEnum::test_function', `ty` is the `test_mod::TestEnum` expression type
19 /// and `name` is the `test_function` 17 /// and `name` is the `test_function`
@@ -25,25 +23,26 @@ pub(crate) enum ImportCandidate {
25} 23}
26 24
27#[derive(Debug)] 25#[derive(Debug)]
28pub(crate) struct TraitImportCandidate { 26pub struct TraitImportCandidate {
29 pub(crate) ty: hir::Type, 27 pub ty: hir::Type,
30 pub(crate) name: ast::NameRef, 28 pub name: ast::NameRef,
31} 29}
32 30
33#[derive(Debug)] 31#[derive(Debug)]
34pub(crate) struct PathImportCandidate { 32pub struct PathImportCandidate {
35 pub(crate) name: ast::NameRef, 33 pub qualifier: Option<ast::Path>,
34 pub name: ast::NameRef,
36} 35}
37 36
38#[derive(Debug)] 37#[derive(Debug)]
39pub(crate) struct ImportAssets { 38pub struct ImportAssets {
40 import_candidate: ImportCandidate, 39 import_candidate: ImportCandidate,
41 module_with_name_to_import: hir::Module, 40 module_with_name_to_import: hir::Module,
42 syntax_under_caret: SyntaxNode, 41 syntax_under_caret: SyntaxNode,
43} 42}
44 43
45impl ImportAssets { 44impl ImportAssets {
46 pub(crate) fn for_method_call( 45 pub fn for_method_call(
47 method_call: ast::MethodCallExpr, 46 method_call: ast::MethodCallExpr,
48 sema: &Semantics<RootDatabase>, 47 sema: &Semantics<RootDatabase>,
49 ) -> Option<Self> { 48 ) -> Option<Self> {
@@ -56,7 +55,7 @@ impl ImportAssets {
56 }) 55 })
57 } 56 }
58 57
59 pub(crate) fn for_regular_path( 58 pub fn for_regular_path(
60 path_under_caret: ast::Path, 59 path_under_caret: ast::Path,
61 sema: &Semantics<RootDatabase>, 60 sema: &Semantics<RootDatabase>,
62 ) -> Option<Self> { 61 ) -> Option<Self> {
@@ -73,24 +72,23 @@ impl ImportAssets {
73 }) 72 })
74 } 73 }
75 74
76 pub(crate) fn syntax_under_caret(&self) -> &SyntaxNode { 75 pub fn syntax_under_caret(&self) -> &SyntaxNode {
77 &self.syntax_under_caret 76 &self.syntax_under_caret
78 } 77 }
79 78
80 pub(crate) fn import_candidate(&self) -> &ImportCandidate { 79 pub fn import_candidate(&self) -> &ImportCandidate {
81 &self.import_candidate 80 &self.import_candidate
82 } 81 }
83 82
84 fn get_search_query(&self) -> &str { 83 fn get_search_query(&self) -> &str {
85 match &self.import_candidate { 84 match &self.import_candidate {
86 ImportCandidate::UnqualifiedName(candidate) 85 ImportCandidate::Path(candidate) => candidate.name.text(),
87 | ImportCandidate::QualifierStart(candidate) => candidate.name.text(),
88 ImportCandidate::TraitAssocItem(candidate) 86 ImportCandidate::TraitAssocItem(candidate)
89 | ImportCandidate::TraitMethod(candidate) => candidate.name.text(), 87 | ImportCandidate::TraitMethod(candidate) => candidate.name.text(),
90 } 88 }
91 } 89 }
92 90
93 pub(crate) fn search_for_imports( 91 pub fn search_for_imports(
94 &self, 92 &self,
95 sema: &Semantics<RootDatabase>, 93 sema: &Semantics<RootDatabase>,
96 config: &InsertUseConfig, 94 config: &InsertUseConfig,
@@ -101,7 +99,7 @@ impl ImportAssets {
101 99
102 /// This may return non-absolute paths if a part of the returned path is already imported into scope. 100 /// This may return non-absolute paths if a part of the returned path is already imported into scope.
103 #[allow(dead_code)] 101 #[allow(dead_code)]
104 pub(crate) fn search_for_relative_paths( 102 pub fn search_for_relative_paths(
105 &self, 103 &self,
106 sema: &Semantics<RootDatabase>, 104 sema: &Semantics<RootDatabase>,
107 ) -> Vec<(hir::ModPath, hir::ItemInNs)> { 105 ) -> Vec<(hir::ModPath, hir::ItemInNs)> {
@@ -253,10 +251,14 @@ impl ImportCandidate {
253 _ => return None, 251 _ => return None,
254 } 252 }
255 } else { 253 } else {
256 ImportCandidate::QualifierStart(PathImportCandidate { name: qualifier_start }) 254 ImportCandidate::Path(PathImportCandidate {
255 qualifier: Some(qualifier),
256 name: qualifier_start,
257 })
257 } 258 }
258 } else { 259 } else {
259 ImportCandidate::UnqualifiedName(PathImportCandidate { 260 ImportCandidate::Path(PathImportCandidate {
261 qualifier: None,
260 name: segment.syntax().descendants().find_map(ast::NameRef::cast)?, 262 name: segment.syntax().descendants().find_map(ast::NameRef::cast)?,
261 }) 263 })
262 }; 264 };
diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs
index 0c180e9bc..877d4f1c7 100644
--- a/crates/ide_db/src/helpers/insert_use.rs
+++ b/crates/ide_db/src/helpers/insert_use.rs
@@ -15,6 +15,12 @@ use syntax::{
15}; 15};
16use test_utils::mark; 16use test_utils::mark;
17 17
18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
19pub struct InsertUseConfig {
20 pub merge: Option<MergeBehavior>,
21 pub prefix_kind: hir::PrefixKind,
22}
23
18#[derive(Debug, Clone)] 24#[derive(Debug, Clone)]
19pub enum ImportScope { 25pub enum ImportScope {
20 File(ast::SourceFile), 26 File(ast::SourceFile),
@@ -97,7 +103,7 @@ pub fn insert_use<'a>(
97) -> SyntaxRewriter<'a> { 103) -> SyntaxRewriter<'a> {
98 let _p = profile::span("insert_use"); 104 let _p = profile::span("insert_use");
99 let mut rewriter = SyntaxRewriter::default(); 105 let mut rewriter = SyntaxRewriter::default();
100 let use_item = make::use_(make::use_tree(path.clone(), None, None, false)); 106 let use_item = make::use_(None, make::use_tree(path.clone(), None, None, false));
101 // merge into existing imports if possible 107 // merge into existing imports if possible
102 if let Some(mb) = merge { 108 if let Some(mb) = merge {
103 for existing_use in scope.as_syntax_node().children().filter_map(ast::Use::cast) { 109 for existing_use in scope.as_syntax_node().children().filter_map(ast::Use::cast) {
diff --git a/crates/ide_db/src/helpers/insert_use/tests.rs b/crates/ide_db/src/helpers/insert_use/tests.rs
index a603fe87f..4bbe66f1f 100644
--- a/crates/ide_db/src/helpers/insert_use/tests.rs
+++ b/crates/ide_db/src/helpers/insert_use/tests.rs
@@ -599,7 +599,7 @@ fn check(
599 599
600 let rewriter = insert_use(&file, path, mb); 600 let rewriter = insert_use(&file, path, mb);
601 let result = rewriter.rewrite(file.as_syntax_node()).to_string(); 601 let result = rewriter.rewrite(file.as_syntax_node()).to_string();
602 assert_eq_text!(&result, ra_fixture_after); 602 assert_eq_text!(ra_fixture_after, &result);
603} 603}
604 604
605fn check_full(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { 605fn check_full(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
diff --git a/crates/ide_db/src/imports_locator.rs b/crates/ide_db/src/imports_locator.rs
index e9f23adf8..d111fba92 100644
--- a/crates/ide_db/src/imports_locator.rs
+++ b/crates/ide_db/src/imports_locator.rs
@@ -12,6 +12,8 @@ use crate::{
12use either::Either; 12use either::Either;
13use rustc_hash::FxHashSet; 13use rustc_hash::FxHashSet;
14 14
15const QUERY_SEARCH_LIMIT: usize = 40;
16
15pub fn find_exact_imports<'a>( 17pub fn find_exact_imports<'a>(
16 sema: &Semantics<'a, RootDatabase>, 18 sema: &Semantics<'a, RootDatabase>,
17 krate: Crate, 19 krate: Crate,
@@ -24,11 +26,11 @@ pub fn find_exact_imports<'a>(
24 { 26 {
25 let mut local_query = symbol_index::Query::new(name_to_import.clone()); 27 let mut local_query = symbol_index::Query::new(name_to_import.clone());
26 local_query.exact(); 28 local_query.exact();
27 local_query.limit(40); 29 local_query.limit(QUERY_SEARCH_LIMIT);
28 local_query 30 local_query
29 }, 31 },
30 import_map::Query::new(name_to_import) 32 import_map::Query::new(name_to_import)
31 .limit(40) 33 .limit(QUERY_SEARCH_LIMIT)
32 .name_only() 34 .name_only()
33 .search_mode(import_map::SearchMode::Equals) 35 .search_mode(import_map::SearchMode::Equals)
34 .case_sensitive(), 36 .case_sensitive(),
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs
index 9ff901e97..bd2977ebd 100644
--- a/crates/mbe/src/tests.rs
+++ b/crates/mbe/src/tests.rs
@@ -261,7 +261,6 @@ fn test_expr_order() {
261 261
262 let dump = format!("{:#?}", expanded); 262 let dump = format!("{:#?}", expanded);
263 assert_eq_text!( 263 assert_eq_text!(
264 dump.trim(),
265 r#"[email protected] 264 r#"[email protected]
266 [email protected] 265 [email protected]
267 [email protected] "fn" 266 [email protected] "fn"
@@ -285,6 +284,7 @@ fn test_expr_order() {
285 [email protected] "2" 284 [email protected] "2"
286 [email protected] ";" 285 [email protected] ";"
287 [email protected] "}""#, 286 [email protected] "}""#,
287 dump.trim()
288 ); 288 );
289} 289}
290 290
@@ -989,7 +989,6 @@ fn test_tt_composite2() {
989 989
990 let res = format!("{:#?}", &node); 990 let res = format!("{:#?}", &node);
991 assert_eq_text!( 991 assert_eq_text!(
992 res.trim(),
993 r###"[email protected] 992 r###"[email protected]
994 [email protected] 993 [email protected]
995 [email protected] 994 [email protected]
@@ -1003,7 +1002,8 @@ fn test_tt_composite2() {
1003 [email protected] ">" 1002 [email protected] ">"
1004 [email protected] " " 1003 [email protected] " "
1005 [email protected] "#" 1004 [email protected] "#"
1006 [email protected] ")""### 1005 [email protected] ")""###,
1006 res.trim()
1007 ); 1007 );
1008} 1008}
1009 1009
@@ -1742,7 +1742,7 @@ impl MacroFixture {
1742 fn assert_expand(&self, invocation: &str, expected: &str) { 1742 fn assert_expand(&self, invocation: &str, expected: &str) {
1743 let expansion = self.expand_tt(invocation); 1743 let expansion = self.expand_tt(invocation);
1744 let actual = format!("{:?}", expansion); 1744 let actual = format!("{:?}", expansion);
1745 test_utils::assert_eq_text!(&actual.trim(), &expected.trim()); 1745 test_utils::assert_eq_text!(&expected.trim(), &actual.trim());
1746 } 1746 }
1747 1747
1748 fn assert_expand_items(&self, invocation: &str, expected: &str) -> &MacroFixture { 1748 fn assert_expand_items(&self, invocation: &str, expected: &str) -> &MacroFixture {
@@ -1941,7 +1941,6 @@ fn test_no_space_after_semi_colon() {
1941 1941
1942 let dump = format!("{:#?}", expanded); 1942 let dump = format!("{:#?}", expanded);
1943 assert_eq_text!( 1943 assert_eq_text!(
1944 dump.trim(),
1945 r###"[email protected] 1944 r###"[email protected]
1946 [email protected] 1945 [email protected]
1947 [email protected] 1946 [email protected]
@@ -1981,6 +1980,7 @@ fn test_no_space_after_semi_colon() {
1981 [email protected] 1980 [email protected]
1982 [email protected] "f" 1981 [email protected] "f"
1983 [email protected] ";""###, 1982 [email protected] ";""###,
1983 dump.trim()
1984 ); 1984 );
1985} 1985}
1986 1986
diff --git a/crates/parser/src/syntax_kind.rs b/crates/parser/src/syntax_kind.rs
index 63204436c..9ea0e4f9b 100644
--- a/crates/parser/src/syntax_kind.rs
+++ b/crates/parser/src/syntax_kind.rs
@@ -6,6 +6,7 @@ mod generated;
6pub use self::generated::SyntaxKind; 6pub use self::generated::SyntaxKind;
7 7
8impl From<u16> for SyntaxKind { 8impl From<u16> for SyntaxKind {
9 #[inline]
9 fn from(d: u16) -> SyntaxKind { 10 fn from(d: u16) -> SyntaxKind {
10 assert!(d <= (SyntaxKind::__LAST as u16)); 11 assert!(d <= (SyntaxKind::__LAST as u16));
11 unsafe { std::mem::transmute::<u16, SyntaxKind>(d) } 12 unsafe { std::mem::transmute::<u16, SyntaxKind>(d) }
@@ -13,12 +14,14 @@ impl From<u16> for SyntaxKind {
13} 14}
14 15
15impl From<SyntaxKind> for u16 { 16impl From<SyntaxKind> for u16 {
17 #[inline]
16 fn from(k: SyntaxKind) -> u16 { 18 fn from(k: SyntaxKind) -> u16 {
17 k as u16 19 k as u16
18 } 20 }
19} 21}
20 22
21impl SyntaxKind { 23impl SyntaxKind {
24 #[inline]
22 pub fn is_trivia(self) -> bool { 25 pub fn is_trivia(self) -> bool {
23 matches!(self, SyntaxKind::WHITESPACE | SyntaxKind::COMMENT) 26 matches!(self, SyntaxKind::WHITESPACE | SyntaxKind::COMMENT)
24 } 27 }
diff --git a/crates/proc_macro_api/Cargo.toml b/crates/proc_macro_api/Cargo.toml
index 1af2bbca7..078568cb2 100644
--- a/crates/proc_macro_api/Cargo.toml
+++ b/crates/proc_macro_api/Cargo.toml
@@ -11,7 +11,7 @@ doctest = false
11 11
12[dependencies] 12[dependencies]
13serde = { version = "1.0", features = ["derive"] } 13serde = { version = "1.0", features = ["derive"] }
14serde_json = "1.0" 14serde_json = { version = "1.0", features = ["unbounded_depth"] }
15log = "0.4.8" 15log = "0.4.8"
16crossbeam-channel = "0.5.0" 16crossbeam-channel = "0.5.0"
17jod-thread = "0.1.1" 17jod-thread = "0.1.1"
diff --git a/crates/proc_macro_srv/src/tests/mod.rs b/crates/proc_macro_srv/src/tests/mod.rs
index 1a827cbd7..fd54f8dfd 100644
--- a/crates/proc_macro_srv/src/tests/mod.rs
+++ b/crates/proc_macro_srv/src/tests/mod.rs
@@ -38,9 +38,9 @@ fn test_derive_proc_macro_list() {
38 let res = list("serde_derive", "1").join("\n"); 38 let res = list("serde_derive", "1").join("\n");
39 39
40 assert_eq_text!( 40 assert_eq_text!(
41 &res,
42 r#"Serialize [CustomDerive] 41 r#"Serialize [CustomDerive]
43Deserialize [CustomDerive]"# 42Deserialize [CustomDerive]"#,
43 &res
44 ); 44 );
45} 45}
46 46
@@ -50,9 +50,9 @@ fn list_test_macros() {
50 let res = list("proc_macro_test", "0.0.0").join("\n"); 50 let res = list("proc_macro_test", "0.0.0").join("\n");
51 51
52 assert_eq_text!( 52 assert_eq_text!(
53 &res,
54 r#"function_like_macro [FuncLike] 53 r#"function_like_macro [FuncLike]
55attribute_macro [Attr] 54attribute_macro [Attr]
56DummyTrait [CustomDerive]"# 55DummyTrait [CustomDerive]"#,
56 &res
57 ); 57 );
58} 58}
diff --git a/crates/proc_macro_srv/src/tests/utils.rs b/crates/proc_macro_srv/src/tests/utils.rs
index 36942147d..196abb8fc 100644
--- a/crates/proc_macro_srv/src/tests/utils.rs
+++ b/crates/proc_macro_srv/src/tests/utils.rs
@@ -52,7 +52,7 @@ pub fn assert_expand(
52 let fixture = parse_string(ra_fixture).unwrap(); 52 let fixture = parse_string(ra_fixture).unwrap();
53 53
54 let res = expander.expand(macro_name, &fixture.subtree, None).unwrap(); 54 let res = expander.expand(macro_name, &fixture.subtree, None).unwrap();
55 assert_eq_text!(&format!("{:?}", res), &expect.trim()); 55 assert_eq_text!(&expect.trim(), &format!("{:?}", res));
56} 56}
57 57
58pub fn list(crate_name: &str, version: &str) -> Vec<String> { 58pub fn list(crate_name: &str, version: &str) -> Vec<String> {
diff --git a/crates/profile/Cargo.toml b/crates/profile/Cargo.toml
index 096233a09..9c7ce26be 100644
--- a/crates/profile/Cargo.toml
+++ b/crates/profile/Cargo.toml
@@ -13,7 +13,7 @@ doctest = false
13once_cell = "1.3.1" 13once_cell = "1.3.1"
14cfg-if = "1" 14cfg-if = "1"
15libc = "0.2.73" 15libc = "0.2.73"
16la-arena = "0.1.0" 16la-arena = { version = "0.2.0", path = "../../lib/arena" }
17 17
18[target.'cfg(target_os = "linux")'.dependencies] 18[target.'cfg(target_os = "linux")'.dependencies]
19perf-event = "0.4" 19perf-event = "0.4"
diff --git a/crates/project_model/Cargo.toml b/crates/project_model/Cargo.toml
index 51e7a7070..293cb5bfe 100644
--- a/crates/project_model/Cargo.toml
+++ b/crates/project_model/Cargo.toml
@@ -17,7 +17,7 @@ serde = { version = "1.0.106", features = ["derive"] }
17serde_json = "1.0.48" 17serde_json = "1.0.48"
18anyhow = "1.0.26" 18anyhow = "1.0.26"
19itertools = "0.10.0" 19itertools = "0.10.0"
20la-arena = "0.1.0" 20la-arena = { version = "0.2.0", path = "../../lib/arena" }
21 21
22cfg = { path = "../cfg", version = "0.0.0" } 22cfg = { path = "../cfg", version = "0.0.0" }
23base_db = { path = "../base_db", version = "0.0.0" } 23base_db = { path = "../base_db", version = "0.0.0" }
@@ -25,3 +25,4 @@ toolchain = { path = "../toolchain", version = "0.0.0" }
25proc_macro_api = { path = "../proc_macro_api", version = "0.0.0" } 25proc_macro_api = { path = "../proc_macro_api", version = "0.0.0" }
26paths = { path = "../paths", version = "0.0.0" } 26paths = { path = "../paths", version = "0.0.0" }
27stdx = { path = "../stdx", version = "0.0.0" } 27stdx = { path = "../stdx", version = "0.0.0" }
28profile = { path = "../profile", version = "0.0.0" }
diff --git a/crates/project_model/src/lib.rs b/crates/project_model/src/lib.rs
index aabb7a47d..970a7e140 100644
--- a/crates/project_model/src/lib.rs
+++ b/crates/project_model/src/lib.rs
@@ -5,6 +5,7 @@ mod cfg_flag;
5mod project_json; 5mod project_json;
6mod sysroot; 6mod sysroot;
7mod workspace; 7mod workspace;
8mod rustc_cfg;
8 9
9use std::{ 10use std::{
10 fs::{read_dir, ReadDir}, 11 fs::{read_dir, ReadDir},
diff --git a/crates/project_model/src/rustc_cfg.rs b/crates/project_model/src/rustc_cfg.rs
new file mode 100644
index 000000000..4a7bd8ae3
--- /dev/null
+++ b/crates/project_model/src/rustc_cfg.rs
@@ -0,0 +1,34 @@
1//! Runs `rustc --print cfg` to get built-in cfg flags.
2
3use std::process::Command;
4
5use crate::{cfg_flag::CfgFlag, utf8_stdout};
6
7pub(crate) fn get(target: Option<&str>) -> Vec<CfgFlag> {
8 let _p = profile::span("rustc_cfg::get");
9 let mut res = Vec::new();
10
11 // Some nightly-only cfgs, which are required for stdlib
12 res.push(CfgFlag::Atom("target_thread_local".into()));
13 for &ty in ["8", "16", "32", "64", "cas", "ptr"].iter() {
14 for &key in ["target_has_atomic", "target_has_atomic_load_store"].iter() {
15 res.push(CfgFlag::KeyValue { key: key.to_string(), value: ty.into() });
16 }
17 }
18
19 let rustc_cfgs = {
20 let mut cmd = Command::new(toolchain::rustc());
21 cmd.args(&["--print", "cfg", "-O"]);
22 if let Some(target) = target {
23 cmd.args(&["--target", target]);
24 }
25 utf8_stdout(cmd)
26 };
27
28 match rustc_cfgs {
29 Ok(rustc_cfgs) => res.extend(rustc_cfgs.lines().map(|it| it.parse().unwrap())),
30 Err(e) => log::error!("failed to get rustc cfgs: {:#}", e),
31 }
32
33 res
34}
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index 06a0be284..8e0481ae9 100644
--- a/crates/project_model/src/workspace.rs
+++ b/crates/project_model/src/workspace.rs
@@ -16,7 +16,7 @@ use proc_macro_api::ProcMacroClient;
16use rustc_hash::{FxHashMap, FxHashSet}; 16use rustc_hash::{FxHashMap, FxHashSet};
17 17
18use crate::{ 18use crate::{
19 cargo_workspace, cfg_flag::CfgFlag, sysroot::SysrootCrate, utf8_stdout, CargoConfig, 19 cargo_workspace, cfg_flag::CfgFlag, rustc_cfg, sysroot::SysrootCrate, utf8_stdout, CargoConfig,
20 CargoWorkspace, ProjectJson, ProjectManifest, Sysroot, TargetKind, 20 CargoWorkspace, ProjectJson, ProjectManifest, Sysroot, TargetKind,
21}; 21};
22 22
@@ -34,15 +34,25 @@ pub struct PackageRoot {
34#[derive(Clone, Eq, PartialEq)] 34#[derive(Clone, Eq, PartialEq)]
35pub enum ProjectWorkspace { 35pub enum ProjectWorkspace {
36 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`. 36 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
37 Cargo { cargo: CargoWorkspace, sysroot: Sysroot, rustc: Option<CargoWorkspace> }, 37 Cargo {
38 cargo: CargoWorkspace,
39 sysroot: Sysroot,
40 rustc: Option<CargoWorkspace>,
41 /// Holds cfg flags for the current target. We get those by running
42 /// `rustc --print cfg`.
43 ///
44 /// FIXME: make this a per-crate map, as, eg, build.rs might have a
45 /// different target.
46 rustc_cfg: Vec<CfgFlag>,
47 },
38 /// Project workspace was manually specified using a `rust-project.json` file. 48 /// Project workspace was manually specified using a `rust-project.json` file.
39 Json { project: ProjectJson, sysroot: Option<Sysroot> }, 49 Json { project: ProjectJson, sysroot: Option<Sysroot>, rustc_cfg: Vec<CfgFlag> },
40} 50}
41 51
42impl fmt::Debug for ProjectWorkspace { 52impl fmt::Debug for ProjectWorkspace {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 53 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 match self { 54 match self {
45 ProjectWorkspace::Cargo { cargo, sysroot, rustc } => f 55 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } => f
46 .debug_struct("Cargo") 56 .debug_struct("Cargo")
47 .field("n_packages", &cargo.packages().len()) 57 .field("n_packages", &cargo.packages().len())
48 .field("n_sysroot_crates", &sysroot.crates().len()) 58 .field("n_sysroot_crates", &sysroot.crates().len())
@@ -50,13 +60,15 @@ impl fmt::Debug for ProjectWorkspace {
50 "n_rustc_compiler_crates", 60 "n_rustc_compiler_crates",
51 &rustc.as_ref().map_or(0, |rc| rc.packages().len()), 61 &rustc.as_ref().map_or(0, |rc| rc.packages().len()),
52 ) 62 )
63 .field("rustc_cfg", rustc_cfg)
53 .finish(), 64 .finish(),
54 ProjectWorkspace::Json { project, sysroot } => { 65 ProjectWorkspace::Json { project, sysroot, rustc_cfg } => {
55 let mut debug_struct = f.debug_struct("Json"); 66 let mut debug_struct = f.debug_struct("Json");
56 debug_struct.field("n_crates", &project.n_crates()); 67 debug_struct.field("n_crates", &project.n_crates());
57 if let Some(sysroot) = sysroot { 68 if let Some(sysroot) = sysroot {
58 debug_struct.field("n_sysroot_crates", &sysroot.crates().len()); 69 debug_struct.field("n_sysroot_crates", &sysroot.crates().len());
59 } 70 }
71 debug_struct.field("rustc_cfg", rustc_cfg);
60 debug_struct.finish() 72 debug_struct.finish()
61 } 73 }
62 } 74 }
@@ -79,7 +91,7 @@ impl ProjectWorkspace {
79 })?; 91 })?;
80 let project_location = project_json.parent().unwrap().to_path_buf(); 92 let project_location = project_json.parent().unwrap().to_path_buf();
81 let project_json = ProjectJson::new(&project_location, data); 93 let project_json = ProjectJson::new(&project_location, data);
82 ProjectWorkspace::load_inline(project_json)? 94 ProjectWorkspace::load_inline(project_json, config.target.as_deref())?
83 } 95 }
84 ProjectManifest::CargoToml(cargo_toml) => { 96 ProjectManifest::CargoToml(cargo_toml) => {
85 let cargo_version = utf8_stdout({ 97 let cargo_version = utf8_stdout({
@@ -117,21 +129,24 @@ impl ProjectWorkspace {
117 } else { 129 } else {
118 None 130 None
119 }; 131 };
120 132 let rustc_cfg = rustc_cfg::get(config.target.as_deref());
121 ProjectWorkspace::Cargo { cargo, sysroot, rustc } 133 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg }
122 } 134 }
123 }; 135 };
124 136
125 Ok(res) 137 Ok(res)
126 } 138 }
127 139
128 pub fn load_inline(project_json: ProjectJson) -> Result<ProjectWorkspace> { 140 pub fn load_inline(
141 project_json: ProjectJson,
142 target: Option<&str>,
143 ) -> Result<ProjectWorkspace> {
129 let sysroot = match &project_json.sysroot_src { 144 let sysroot = match &project_json.sysroot_src {
130 Some(path) => Some(Sysroot::load(path)?), 145 Some(path) => Some(Sysroot::load(path)?),
131 None => None, 146 None => None,
132 }; 147 };
133 148 let rustc_cfg = rustc_cfg::get(target);
134 Ok(ProjectWorkspace::Json { project: project_json, sysroot }) 149 Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg })
135 } 150 }
136 151
137 /// Returns the roots for the current `ProjectWorkspace` 152 /// Returns the roots for the current `ProjectWorkspace`
@@ -139,7 +154,7 @@ impl ProjectWorkspace {
139 /// the root is a member of the current workspace 154 /// the root is a member of the current workspace
140 pub fn to_roots(&self) -> Vec<PackageRoot> { 155 pub fn to_roots(&self) -> Vec<PackageRoot> {
141 match self { 156 match self {
142 ProjectWorkspace::Json { project, sysroot } => project 157 ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project
143 .crates() 158 .crates()
144 .map(|(_, krate)| PackageRoot { 159 .map(|(_, krate)| PackageRoot {
145 is_member: krate.is_workspace_member, 160 is_member: krate.is_workspace_member,
@@ -156,7 +171,7 @@ impl ProjectWorkspace {
156 }) 171 })
157 })) 172 }))
158 .collect::<Vec<_>>(), 173 .collect::<Vec<_>>(),
159 ProjectWorkspace::Cargo { cargo, sysroot, rustc } => cargo 174 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg: _ } => cargo
160 .packages() 175 .packages()
161 .map(|pkg| { 176 .map(|pkg| {
162 let is_member = cargo[pkg].is_member; 177 let is_member = cargo[pkg].is_member;
@@ -194,7 +209,7 @@ impl ProjectWorkspace {
194 pub fn n_packages(&self) -> usize { 209 pub fn n_packages(&self) -> usize {
195 match self { 210 match self {
196 ProjectWorkspace::Json { project, .. } => project.n_crates(), 211 ProjectWorkspace::Json { project, .. } => project.n_crates(),
197 ProjectWorkspace::Cargo { cargo, sysroot, rustc } => { 212 ProjectWorkspace::Cargo { cargo, sysroot, rustc, .. } => {
198 let rustc_package_len = rustc.as_ref().map_or(0, |rc| rc.packages().len()); 213 let rustc_package_len = rustc.as_ref().map_or(0, |rc| rc.packages().len());
199 cargo.packages().len() + sysroot.crates().len() + rustc_package_len 214 cargo.packages().len() + sysroot.crates().len() + rustc_package_len
200 } 215 }
@@ -203,22 +218,31 @@ impl ProjectWorkspace {
203 218
204 pub fn to_crate_graph( 219 pub fn to_crate_graph(
205 &self, 220 &self,
206 target: Option<&str>,
207 proc_macro_client: Option<&ProcMacroClient>, 221 proc_macro_client: Option<&ProcMacroClient>,
208 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, 222 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
209 ) -> CrateGraph { 223 ) -> CrateGraph {
224 let _p = profile::span("ProjectWorkspace::to_crate_graph");
210 let proc_macro_loader = |path: &Path| match proc_macro_client { 225 let proc_macro_loader = |path: &Path| match proc_macro_client {
211 Some(client) => client.by_dylib_path(path), 226 Some(client) => client.by_dylib_path(path),
212 None => Vec::new(), 227 None => Vec::new(),
213 }; 228 };
214 229
215 let mut crate_graph = match self { 230 let mut crate_graph = match self {
216 ProjectWorkspace::Json { project, sysroot } => { 231 ProjectWorkspace::Json { project, sysroot, rustc_cfg } => project_json_to_crate_graph(
217 project_json_to_crate_graph(target, &proc_macro_loader, load, project, sysroot) 232 rustc_cfg.clone(),
218 } 233 &proc_macro_loader,
219 ProjectWorkspace::Cargo { cargo, sysroot, rustc } => { 234 load,
220 cargo_to_crate_graph(target, &proc_macro_loader, load, cargo, sysroot, rustc) 235 project,
221 } 236 sysroot,
237 ),
238 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } => cargo_to_crate_graph(
239 rustc_cfg.clone(),
240 &proc_macro_loader,
241 load,
242 cargo,
243 sysroot,
244 rustc,
245 ),
222 }; 246 };
223 if crate_graph.patch_cfg_if() { 247 if crate_graph.patch_cfg_if() {
224 log::debug!("Patched std to depend on cfg-if") 248 log::debug!("Patched std to depend on cfg-if")
@@ -230,7 +254,7 @@ impl ProjectWorkspace {
230} 254}
231 255
232fn project_json_to_crate_graph( 256fn project_json_to_crate_graph(
233 target: Option<&str>, 257 rustc_cfg: Vec<CfgFlag>,
234 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, 258 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
235 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, 259 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
236 project: &ProjectJson, 260 project: &ProjectJson,
@@ -239,9 +263,9 @@ fn project_json_to_crate_graph(
239 let mut crate_graph = CrateGraph::default(); 263 let mut crate_graph = CrateGraph::default();
240 let sysroot_deps = sysroot 264 let sysroot_deps = sysroot
241 .as_ref() 265 .as_ref()
242 .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load)); 266 .map(|sysroot| sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load));
243 267
244 let mut cfg_cache: FxHashMap<Option<&str>, Vec<CfgFlag>> = FxHashMap::default(); 268 let mut cfg_cache: FxHashMap<&str, Vec<CfgFlag>> = FxHashMap::default();
245 let crates: FxHashMap<CrateId, CrateId> = project 269 let crates: FxHashMap<CrateId, CrateId> = project
246 .crates() 270 .crates()
247 .filter_map(|(crate_id, krate)| { 271 .filter_map(|(crate_id, krate)| {
@@ -253,9 +277,12 @@ fn project_json_to_crate_graph(
253 let env = krate.env.clone().into_iter().collect(); 277 let env = krate.env.clone().into_iter().collect();
254 let proc_macro = krate.proc_macro_dylib_path.clone().map(|it| proc_macro_loader(&it)); 278 let proc_macro = krate.proc_macro_dylib_path.clone().map(|it| proc_macro_loader(&it));
255 279
256 let target = krate.target.as_deref().or(target); 280 let target_cfgs = match krate.target.as_deref() {
257 let target_cfgs = 281 Some(target) => {
258 cfg_cache.entry(target).or_insert_with(|| get_rustc_cfg_options(target)); 282 cfg_cache.entry(target).or_insert_with(|| rustc_cfg::get(Some(target)))
283 }
284 None => &rustc_cfg,
285 };
259 286
260 let mut cfg_options = CfgOptions::default(); 287 let mut cfg_options = CfgOptions::default();
261 cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned()); 288 cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
@@ -292,19 +319,20 @@ fn project_json_to_crate_graph(
292} 319}
293 320
294fn cargo_to_crate_graph( 321fn cargo_to_crate_graph(
295 target: Option<&str>, 322 rustc_cfg: Vec<CfgFlag>,
296 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, 323 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
297 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, 324 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
298 cargo: &CargoWorkspace, 325 cargo: &CargoWorkspace,
299 sysroot: &Sysroot, 326 sysroot: &Sysroot,
300 rustc: &Option<CargoWorkspace>, 327 rustc: &Option<CargoWorkspace>,
301) -> CrateGraph { 328) -> CrateGraph {
329 let _p = profile::span("cargo_to_crate_graph");
302 let mut crate_graph = CrateGraph::default(); 330 let mut crate_graph = CrateGraph::default();
303 let (public_deps, libproc_macro) = 331 let (public_deps, libproc_macro) =
304 sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load); 332 sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load);
305 333
306 let mut cfg_options = CfgOptions::default(); 334 let mut cfg_options = CfgOptions::default();
307 cfg_options.extend(get_rustc_cfg_options(target)); 335 cfg_options.extend(rustc_cfg);
308 336
309 let mut pkg_to_lib_crate = FxHashMap::default(); 337 let mut pkg_to_lib_crate = FxHashMap::default();
310 338
@@ -490,11 +518,12 @@ fn add_target_crate_root(
490fn sysroot_to_crate_graph( 518fn sysroot_to_crate_graph(
491 crate_graph: &mut CrateGraph, 519 crate_graph: &mut CrateGraph,
492 sysroot: &Sysroot, 520 sysroot: &Sysroot,
493 target: Option<&str>, 521 rustc_cfg: Vec<CfgFlag>,
494 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, 522 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
495) -> (Vec<(CrateName, CrateId)>, Option<CrateId>) { 523) -> (Vec<(CrateName, CrateId)>, Option<CrateId>) {
524 let _p = profile::span("sysroot_to_crate_graph");
496 let mut cfg_options = CfgOptions::default(); 525 let mut cfg_options = CfgOptions::default();
497 cfg_options.extend(get_rustc_cfg_options(target)); 526 cfg_options.extend(rustc_cfg);
498 let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot 527 let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot
499 .crates() 528 .crates()
500 .filter_map(|krate| { 529 .filter_map(|krate| {
@@ -533,34 +562,6 @@ fn sysroot_to_crate_graph(
533 (public_deps, libproc_macro) 562 (public_deps, libproc_macro)
534} 563}
535 564
536fn get_rustc_cfg_options(target: Option<&str>) -> Vec<CfgFlag> {
537 let mut res = Vec::new();
538
539 // Some nightly-only cfgs, which are required for stdlib
540 res.push(CfgFlag::Atom("target_thread_local".into()));
541 for &ty in ["8", "16", "32", "64", "cas", "ptr"].iter() {
542 for &key in ["target_has_atomic", "target_has_atomic_load_store"].iter() {
543 res.push(CfgFlag::KeyValue { key: key.to_string(), value: ty.into() });
544 }
545 }
546
547 let rustc_cfgs = {
548 let mut cmd = Command::new(toolchain::rustc());
549 cmd.args(&["--print", "cfg", "-O"]);
550 if let Some(target) = target {
551 cmd.args(&["--target", target]);
552 }
553 utf8_stdout(cmd)
554 };
555
556 match rustc_cfgs {
557 Ok(rustc_cfgs) => res.extend(rustc_cfgs.lines().map(|it| it.parse().unwrap())),
558 Err(e) => log::error!("failed to get rustc cfgs: {:#}", e),
559 }
560
561 res
562}
563
564fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) { 565fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) {
565 if let Err(err) = graph.add_dep(from, name, to) { 566 if let Err(err) = graph.add_dep(from, name, to) {
566 log::error!("{}", err) 567 log::error!("{}", err)
diff --git a/crates/rust-analyzer/src/cli/analysis_bench.rs b/crates/rust-analyzer/src/cli/analysis_bench.rs
index 7d3fda7a8..a02c8327f 100644
--- a/crates/rust-analyzer/src/cli/analysis_bench.rs
+++ b/crates/rust-analyzer/src/cli/analysis_bench.rs
@@ -3,6 +3,7 @@
3use std::{env, path::PathBuf, str::FromStr, sync::Arc, time::Instant}; 3use std::{env, path::PathBuf, str::FromStr, sync::Arc, time::Instant};
4 4
5use anyhow::{bail, format_err, Result}; 5use anyhow::{bail, format_err, Result};
6use hir::PrefixKind;
6use ide::{ 7use ide::{
7 Analysis, AnalysisHost, Change, CompletionConfig, DiagnosticsConfig, FilePosition, LineCol, 8 Analysis, AnalysisHost, Change, CompletionConfig, DiagnosticsConfig, FilePosition, LineCol,
8}; 9};
@@ -11,7 +12,7 @@ use ide_db::{
11 salsa::{Database, Durability}, 12 salsa::{Database, Durability},
12 FileId, 13 FileId,
13 }, 14 },
14 helpers::SnippetCap, 15 helpers::{insert_use::InsertUseConfig, SnippetCap},
15}; 16};
16use vfs::AbsPathBuf; 17use vfs::AbsPathBuf;
17 18
@@ -96,7 +97,7 @@ impl BenchCmd {
96 add_call_parenthesis: true, 97 add_call_parenthesis: true,
97 add_call_argument_snippets: true, 98 add_call_argument_snippets: true,
98 snippet_cap: SnippetCap::new(true), 99 snippet_cap: SnippetCap::new(true),
99 merge: None, 100 insert_use: InsertUseConfig { merge: None, prefix_kind: PrefixKind::Plain },
100 }; 101 };
101 let res = do_work(&mut host, file_id, |analysis| { 102 let res = do_work(&mut host, file_id, |analysis| {
102 analysis.completions(&options, file_position) 103 analysis.completions(&options, file_position)
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs
index 31a16ca46..16ccab781 100644
--- a/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -39,7 +39,7 @@ pub fn load_cargo(
39 None 39 None
40 }; 40 };
41 41
42 let crate_graph = ws.to_crate_graph(None, proc_macro_client.as_ref(), &mut |path: &AbsPath| { 42 let crate_graph = ws.to_crate_graph(proc_macro_client.as_ref(), &mut |path: &AbsPath| {
43 let contents = loader.load_sync(path); 43 let contents = loader.load_sync(path);
44 let path = vfs::VfsPath::from(path.to_path_buf()); 44 let path = vfs::VfsPath::from(path.to_path_buf());
45 vfs.set_file_contents(path.clone(), contents); 45 vfs.set_file_contents(path.clone(), contents);
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 27b92a5a9..ce9655818 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -11,11 +11,11 @@ use std::{convert::TryFrom, ffi::OsString, iter, path::PathBuf};
11 11
12use flycheck::FlycheckConfig; 12use flycheck::FlycheckConfig;
13use hir::PrefixKind; 13use hir::PrefixKind;
14use ide::{ 14use ide::{AssistConfig, CompletionConfig, DiagnosticsConfig, HoverConfig, InlayHintsConfig};
15 AssistConfig, CompletionConfig, DiagnosticsConfig, HoverConfig, InlayHintsConfig, 15use ide_db::helpers::{
16 InsertUseConfig, 16 insert_use::{InsertUseConfig, MergeBehavior},
17 SnippetCap,
17}; 18};
18use ide_db::helpers::{insert_use::MergeBehavior, SnippetCap};
19use itertools::Itertools; 19use itertools::Itertools;
20use lsp_types::{ClientCapabilities, MarkupKind}; 20use lsp_types::{ClientCapabilities, MarkupKind};
21use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest}; 21use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest};
@@ -542,11 +542,18 @@ impl Config {
542 max_length: self.data.inlayHints_maxLength, 542 max_length: self.data.inlayHints_maxLength,
543 } 543 }
544 } 544 }
545 fn merge_behavior(&self) -> Option<MergeBehavior> { 545 fn insert_use_config(&self) -> InsertUseConfig {
546 match self.data.assist_importMergeBehavior { 546 InsertUseConfig {
547 MergeBehaviorDef::None => None, 547 merge: match self.data.assist_importMergeBehavior {
548 MergeBehaviorDef::Full => Some(MergeBehavior::Full), 548 MergeBehaviorDef::None => None,
549 MergeBehaviorDef::Last => Some(MergeBehavior::Last), 549 MergeBehaviorDef::Full => Some(MergeBehavior::Full),
550 MergeBehaviorDef::Last => Some(MergeBehavior::Last),
551 },
552 prefix_kind: match self.data.assist_importPrefix {
553 ImportPrefixDef::Plain => PrefixKind::Plain,
554 ImportPrefixDef::ByCrate => PrefixKind::ByCrate,
555 ImportPrefixDef::BySelf => PrefixKind::BySelf,
556 },
550 } 557 }
551 } 558 }
552 pub fn completion(&self) -> CompletionConfig { 559 pub fn completion(&self) -> CompletionConfig {
@@ -556,7 +563,7 @@ impl Config {
556 && completion_item_edit_resolve(&self.caps), 563 && completion_item_edit_resolve(&self.caps),
557 add_call_parenthesis: self.data.completion_addCallParenthesis, 564 add_call_parenthesis: self.data.completion_addCallParenthesis,
558 add_call_argument_snippets: self.data.completion_addCallArgumentSnippets, 565 add_call_argument_snippets: self.data.completion_addCallArgumentSnippets,
559 merge: self.merge_behavior(), 566 insert_use: self.insert_use_config(),
560 snippet_cap: SnippetCap::new(try_or!( 567 snippet_cap: SnippetCap::new(try_or!(
561 self.caps 568 self.caps
562 .text_document 569 .text_document
@@ -575,7 +582,11 @@ impl Config {
575 snippet_cap: SnippetCap::new(self.experimental("snippetTextEdit")), 582 snippet_cap: SnippetCap::new(self.experimental("snippetTextEdit")),
576 allowed: None, 583 allowed: None,
577 insert_use: InsertUseConfig { 584 insert_use: InsertUseConfig {
578 merge: self.merge_behavior(), 585 merge: match self.data.assist_importMergeBehavior {
586 MergeBehaviorDef::None => None,
587 MergeBehaviorDef::Full => Some(MergeBehavior::Full),
588 MergeBehaviorDef::Last => Some(MergeBehavior::Last),
589 },
579 prefix_kind: match self.data.assist_importPrefix { 590 prefix_kind: match self.data.assist_importPrefix {
580 ImportPrefixDef::Plain => PrefixKind::Plain, 591 ImportPrefixDef::Plain => PrefixKind::Plain,
581 ImportPrefixDef::ByCrate => PrefixKind::ByCrate, 592 ImportPrefixDef::ByCrate => PrefixKind::ByCrate,
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index 97e20362f..dabfb4241 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -135,7 +135,10 @@ impl GlobalState {
135 ) 135 )
136 } 136 }
137 LinkedProject::InlineJsonProject(it) => { 137 LinkedProject::InlineJsonProject(it) => {
138 project_model::ProjectWorkspace::load_inline(it.clone()) 138 project_model::ProjectWorkspace::load_inline(
139 it.clone(),
140 cargo_config.target.as_deref(),
141 )
139 } 142 }
140 }) 143 })
141 .collect::<Vec<_>>(); 144 .collect::<Vec<_>>();
@@ -253,11 +256,7 @@ impl GlobalState {
253 res 256 res
254 }; 257 };
255 for ws in workspaces.iter() { 258 for ws in workspaces.iter() {
256 crate_graph.extend(ws.to_crate_graph( 259 crate_graph.extend(ws.to_crate_graph(self.proc_macro_client.as_ref(), &mut load));
257 self.config.cargo().target.as_deref(),
258 self.proc_macro_client.as_ref(),
259 &mut load,
260 ));
261 } 260 }
262 261
263 crate_graph 262 crate_graph
@@ -274,6 +273,7 @@ impl GlobalState {
274 } 273 }
275 274
276 fn reload_flycheck(&mut self) { 275 fn reload_flycheck(&mut self) {
276 let _p = profile::span("GlobalState::reload_flycheck");
277 let config = match self.config.flycheck() { 277 let config = match self.config.flycheck() {
278 Some(it) => it, 278 Some(it) => it,
279 None => { 279 None => {
@@ -288,9 +288,7 @@ impl GlobalState {
288 .iter() 288 .iter()
289 .enumerate() 289 .enumerate()
290 .filter_map(|(id, w)| match w { 290 .filter_map(|(id, w)| match w {
291 ProjectWorkspace::Cargo { cargo, sysroot: _, rustc: _ } => { 291 ProjectWorkspace::Cargo { cargo, .. } => Some((id, cargo.workspace_root())),
292 Some((id, cargo.workspace_root()))
293 }
294 ProjectWorkspace::Json { project, .. } => { 292 ProjectWorkspace::Json { project, .. } => {
295 // Enable flychecks for json projects if a custom flycheck command was supplied 293 // Enable flychecks for json projects if a custom flycheck command was supplied
296 // in the workspace configuration. 294 // in the workspace configuration.
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index dc67d19a7..1ff2d3fea 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -861,8 +861,9 @@ pub(crate) fn rename_error(err: RenameError) -> crate::LspError {
861 861
862#[cfg(test)] 862#[cfg(test)]
863mod tests { 863mod tests {
864 use hir::PrefixKind;
864 use ide::Analysis; 865 use ide::Analysis;
865 use ide_db::helpers::SnippetCap; 866 use ide_db::helpers::{insert_use::InsertUseConfig, SnippetCap};
866 867
867 use super::*; 868 use super::*;
868 869
@@ -887,7 +888,7 @@ mod tests {
887 add_call_parenthesis: true, 888 add_call_parenthesis: true,
888 add_call_argument_snippets: true, 889 add_call_argument_snippets: true,
889 snippet_cap: SnippetCap::new(true), 890 snippet_cap: SnippetCap::new(true),
890 merge: None, 891 insert_use: InsertUseConfig { merge: None, prefix_kind: PrefixKind::Plain },
891 }, 892 },
892 ide_db::base_db::FilePosition { file_id, offset }, 893 ide_db::base_db::FilePosition { file_id, offset },
893 ) 894 )
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index ad8b797fe..52394b337 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -12,7 +12,7 @@ doctest = false
12 12
13[dependencies] 13[dependencies]
14itertools = "0.10.0" 14itertools = "0.10.0"
15rowan = "0.10.1" 15rowan = "0.10.3"
16rustc_lexer = { version = "697.0.0", package = "rustc-ap-rustc_lexer" } 16rustc_lexer = { version = "697.0.0", package = "rustc-ap-rustc_lexer" }
17rustc-hash = "1.1.0" 17rustc-hash = "1.1.0"
18arrayvec = "0.5.1" 18arrayvec = "0.5.1"
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 1ed8a96e5..9ffc3ae11 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -108,8 +108,12 @@ pub fn use_tree_list(use_trees: impl IntoIterator<Item = ast::UseTree>) -> ast::
108 ast_from_text(&format!("use {{{}}};", use_trees)) 108 ast_from_text(&format!("use {{{}}};", use_trees))
109} 109}
110 110
111pub fn use_(use_tree: ast::UseTree) -> ast::Use { 111pub fn use_(visibility: Option<ast::Visibility>, use_tree: ast::UseTree) -> ast::Use {
112 ast_from_text(&format!("use {};", use_tree)) 112 let visibility = match visibility {
113 None => String::new(),
114 Some(it) => format!("{} ", it),
115 };
116 ast_from_text(&format!("{}use {};", visibility, use_tree))
113} 117}
114 118
115pub fn record_expr_field(name: ast::NameRef, expr: Option<ast::Expr>) -> ast::RecordExprField { 119pub fn record_expr_field(name: ast::NameRef, expr: Option<ast::Expr>) -> ast::RecordExprField {