aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock25
-rw-r--r--crates/ra_assists/Cargo.toml3
-rw-r--r--crates/ra_assists/src/handlers/auto_import.rs49
-rw-r--r--crates/ra_assists/src/handlers/fill_match_arms.rs4
-rw-r--r--crates/ra_assists/src/lib.rs29
-rw-r--r--crates/ra_fmt/Cargo.toml2
-rw-r--r--crates/ra_hir/Cargo.toml2
-rw-r--r--crates/ra_hir/src/code_model.rs22
-rw-r--r--crates/ra_hir/src/from_id.rs21
-rw-r--r--crates/ra_ide/Cargo.toml2
-rw-r--r--crates/ra_ide/src/completion/complete_postfix.rs140
-rw-r--r--crates/ra_ide_db/Cargo.toml1
-rw-r--r--crates/ra_ide_db/src/imports_locator.rs8
-rw-r--r--crates/ra_syntax/Cargo.toml2
-rw-r--r--crates/rust-analyzer/Cargo.toml2
-rw-r--r--editors/code/package.json12
-rw-r--r--editors/code/src/client.ts6
-rw-r--r--editors/code/src/config.ts18
-rw-r--r--editors/code/src/main.ts14
19 files changed, 272 insertions, 90 deletions
diff --git a/Cargo.lock b/Cargo.lock
index eb5247b69..908319f87 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -170,7 +170,7 @@ dependencies = [
170 "chalk-macros", 170 "chalk-macros",
171 "chalk-rust-ir", 171 "chalk-rust-ir",
172 "ena", 172 "ena",
173 "itertools", 173 "itertools 0.8.2",
174 "petgraph", 174 "petgraph",
175 "rustc-hash", 175 "rustc-hash",
176] 176]
@@ -521,6 +521,15 @@ dependencies = [
521] 521]
522 522
523[[package]] 523[[package]]
524name = "itertools"
525version = "0.9.0"
526source = "registry+https://github.com/rust-lang/crates.io-index"
527checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
528dependencies = [
529 "either",
530]
531
532[[package]]
524name = "itoa" 533name = "itoa"
525version = "0.4.5" 534version = "0.4.5"
526source = "registry+https://github.com/rust-lang/crates.io-index" 535source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -875,8 +884,9 @@ version = "0.1.0"
875name = "ra_assists" 884name = "ra_assists"
876version = "0.1.0" 885version = "0.1.0"
877dependencies = [ 886dependencies = [
887 "either",
878 "format-buf", 888 "format-buf",
879 "itertools", 889 "itertools 0.9.0",
880 "join_to_string", 890 "join_to_string",
881 "ra_db", 891 "ra_db",
882 "ra_fmt", 892 "ra_fmt",
@@ -928,7 +938,7 @@ dependencies = [
928name = "ra_fmt" 938name = "ra_fmt"
929version = "0.1.0" 939version = "0.1.0"
930dependencies = [ 940dependencies = [
931 "itertools", 941 "itertools 0.9.0",
932 "ra_syntax", 942 "ra_syntax",
933] 943]
934 944
@@ -938,7 +948,7 @@ version = "0.1.0"
938dependencies = [ 948dependencies = [
939 "arrayvec", 949 "arrayvec",
940 "either", 950 "either",
941 "itertools", 951 "itertools 0.9.0",
942 "log", 952 "log",
943 "ra_db", 953 "ra_db",
944 "ra_hir_def", 954 "ra_hir_def",
@@ -1015,7 +1025,7 @@ dependencies = [
1015 "format-buf", 1025 "format-buf",
1016 "indexmap", 1026 "indexmap",
1017 "insta", 1027 "insta",
1018 "itertools", 1028 "itertools 0.9.0",
1019 "join_to_string", 1029 "join_to_string",
1020 "log", 1030 "log",
1021 "ra_assists", 1031 "ra_assists",
@@ -1036,6 +1046,7 @@ dependencies = [
1036name = "ra_ide_db" 1046name = "ra_ide_db"
1037version = "0.1.0" 1047version = "0.1.0"
1038dependencies = [ 1048dependencies = [
1049 "either",
1039 "fst", 1050 "fst",
1040 "log", 1051 "log",
1041 "once_cell", 1052 "once_cell",
@@ -1101,7 +1112,7 @@ name = "ra_syntax"
1101version = "0.1.0" 1112version = "0.1.0"
1102dependencies = [ 1113dependencies = [
1103 "arrayvec", 1114 "arrayvec",
1104 "itertools", 1115 "itertools 0.9.0",
1105 "once_cell", 1116 "once_cell",
1106 "ra_parser", 1117 "ra_parser",
1107 "ra_text_edit", 1118 "ra_text_edit",
@@ -1278,7 +1289,7 @@ dependencies = [
1278 "crossbeam-channel", 1289 "crossbeam-channel",
1279 "env_logger", 1290 "env_logger",
1280 "globset", 1291 "globset",
1281 "itertools", 1292 "itertools 0.9.0",
1282 "jod-thread", 1293 "jod-thread",
1283 "log", 1294 "log",
1284 "lsp-server", 1295 "lsp-server",
diff --git a/crates/ra_assists/Cargo.toml b/crates/ra_assists/Cargo.toml
index 85adddb5b..a87f4052a 100644
--- a/crates/ra_assists/Cargo.toml
+++ b/crates/ra_assists/Cargo.toml
@@ -11,7 +11,8 @@ doctest = false
11format-buf = "1.0.0" 11format-buf = "1.0.0"
12join_to_string = "0.1.3" 12join_to_string = "0.1.3"
13rustc-hash = "1.1.0" 13rustc-hash = "1.1.0"
14itertools = "0.8.2" 14itertools = "0.9.0"
15either = "1.5.3"
15 16
16ra_syntax = { path = "../ra_syntax" } 17ra_syntax = { path = "../ra_syntax" }
17ra_text_edit = { path = "../ra_text_edit" } 18ra_text_edit = { path = "../ra_text_edit" }
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs
index bb280f633..99682e023 100644
--- a/crates/ra_assists/src/handlers/auto_import.rs
+++ b/crates/ra_assists/src/handlers/auto_import.rs
@@ -17,6 +17,7 @@ use crate::{
17 utils::insert_use_statement, 17 utils::insert_use_statement,
18 AssistId, 18 AssistId,
19}; 19};
20use either::Either;
20 21
21// Assist: auto_import 22// Assist: auto_import
22// 23//
@@ -58,6 +59,7 @@ pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> {
58 group.finish() 59 group.finish()
59} 60}
60 61
62#[derive(Debug)]
61struct AutoImportAssets { 63struct AutoImportAssets {
62 import_candidate: ImportCandidate, 64 import_candidate: ImportCandidate,
63 module_with_name_to_import: Module, 65 module_with_name_to_import: Module,
@@ -127,14 +129,14 @@ impl AutoImportAssets {
127 ImportsLocator::new(db) 129 ImportsLocator::new(db)
128 .find_imports(&self.get_search_query()) 130 .find_imports(&self.get_search_query())
129 .into_iter() 131 .into_iter()
130 .filter_map(|module_def| match &self.import_candidate { 132 .filter_map(|candidate| match &self.import_candidate {
131 ImportCandidate::TraitAssocItem(assoc_item_type, _) => { 133 ImportCandidate::TraitAssocItem(assoc_item_type, _) => {
132 let located_assoc_item = match module_def { 134 let located_assoc_item = match candidate {
133 ModuleDef::Function(located_function) => located_function 135 Either::Left(ModuleDef::Function(located_function)) => located_function
134 .as_assoc_item(db) 136 .as_assoc_item(db)
135 .map(|assoc| assoc.container(db)) 137 .map(|assoc| assoc.container(db))
136 .and_then(Self::assoc_to_trait), 138 .and_then(Self::assoc_to_trait),
137 ModuleDef::Const(located_const) => located_const 139 Either::Left(ModuleDef::Const(located_const)) => located_const
138 .as_assoc_item(db) 140 .as_assoc_item(db)
139 .map(|assoc| assoc.container(db)) 141 .map(|assoc| assoc.container(db))
140 .and_then(Self::assoc_to_trait), 142 .and_then(Self::assoc_to_trait),
@@ -153,10 +155,11 @@ impl AutoImportAssets {
153 |_, assoc| Self::assoc_to_trait(assoc.container(db)), 155 |_, assoc| Self::assoc_to_trait(assoc.container(db)),
154 ) 156 )
155 .map(ModuleDef::from) 157 .map(ModuleDef::from)
158 .map(Either::Left)
156 } 159 }
157 ImportCandidate::TraitMethod(function_callee, _) => { 160 ImportCandidate::TraitMethod(function_callee, _) => {
158 let located_assoc_item = 161 let located_assoc_item =
159 if let ModuleDef::Function(located_function) = module_def { 162 if let Either::Left(ModuleDef::Function(located_function)) = candidate {
160 located_function 163 located_function
161 .as_assoc_item(db) 164 .as_assoc_item(db)
162 .map(|assoc| assoc.container(db)) 165 .map(|assoc| assoc.container(db))
@@ -179,10 +182,18 @@ impl AutoImportAssets {
179 }, 182 },
180 ) 183 )
181 .map(ModuleDef::from) 184 .map(ModuleDef::from)
185 .map(Either::Left)
186 }
187 _ => Some(candidate),
188 })
189 .filter_map(|candidate| match candidate {
190 Either::Left(module_def) => {
191 self.module_with_name_to_import.find_use_path(db, module_def)
192 }
193 Either::Right(macro_def) => {
194 self.module_with_name_to_import.find_use_path(db, macro_def)
182 } 195 }
183 _ => Some(module_def),
184 }) 196 })
185 .filter_map(|module_def| self.module_with_name_to_import.find_use_path(db, module_def))
186 .filter(|use_path| !use_path.segments.is_empty()) 197 .filter(|use_path| !use_path.segments.is_empty())
187 .take(20) 198 .take(20)
188 .collect::<BTreeSet<_>>() 199 .collect::<BTreeSet<_>>()
@@ -440,6 +451,30 @@ mod tests {
440 } 451 }
441 452
442 #[test] 453 #[test]
454 fn macro_import() {
455 check_assist(
456 auto_import,
457 r"
458 //- /lib.rs crate:crate_with_macro
459 #[macro_export]
460 macro_rules! foo {
461 () => ()
462 }
463
464 //- /main.rs crate:main deps:crate_with_macro
465 fn main() {
466 foo<|>
467 }",
468 r"use crate_with_macro::foo;
469
470fn main() {
471 foo<|>
472}
473",
474 );
475 }
476
477 #[test]
443 fn auto_import_target() { 478 fn auto_import_target() {
444 check_assist_target( 479 check_assist_target(
445 auto_import, 480 auto_import,
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs
index 7463b2af7..88b4c8926 100644
--- a/crates/ra_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ra_assists/src/handlers/fill_match_arms.rs
@@ -2,7 +2,7 @@
2 2
3use std::iter; 3use std::iter;
4 4
5use hir::{Adt, HasSource, Semantics}; 5use hir::{Adt, HasSource, ModuleDef, Semantics};
6use itertools::Itertools; 6use itertools::Itertools;
7use ra_ide_db::RootDatabase; 7use ra_ide_db::RootDatabase;
8 8
@@ -154,7 +154,7 @@ fn resolve_tuple_of_enum_def(
154} 154}
155 155
156fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> Option<ast::Pat> { 156fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> Option<ast::Pat> {
157 let path = crate::ast_transform::path_to_ast(module.find_use_path(db, var.into())?); 157 let path = crate::ast_transform::path_to_ast(module.find_use_path(db, ModuleDef::from(var))?);
158 158
159 // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though 159 // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though
160 let pat: ast::Pat = match var.source(db).value.kind() { 160 let pat: ast::Pat = match var.source(db).value.kind() {
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index b8704ea7d..bcc9b3f10 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -165,7 +165,6 @@ mod helpers {
165 165
166 use ra_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt}; 166 use ra_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt};
167 use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase}; 167 use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase};
168 use ra_syntax::TextRange;
169 use test_utils::{add_cursor, assert_eq_text, extract_range_or_offset, RangeOrOffset}; 168 use test_utils::{add_cursor, assert_eq_text, extract_range_or_offset, RangeOrOffset};
170 169
171 use crate::{AssistCtx, AssistHandler}; 170 use crate::{AssistCtx, AssistHandler};
@@ -175,8 +174,7 @@ mod helpers {
175 let (mut db, file_id) = RootDatabase::with_single_file(text); 174 let (mut db, file_id) = RootDatabase::with_single_file(text);
176 // FIXME: ideally, this should be done by the above `RootDatabase::with_single_file`, 175 // FIXME: ideally, this should be done by the above `RootDatabase::with_single_file`,
177 // but it looks like this might need specialization? :( 176 // but it looks like this might need specialization? :(
178 let local_roots = vec![db.file_source_root(file_id)]; 177 db.set_local_roots(Arc::new(vec![db.file_source_root(file_id)]));
179 db.set_local_roots(Arc::new(local_roots));
180 (db, file_id) 178 (db, file_id)
181 } 179 }
182 180
@@ -206,11 +204,24 @@ mod helpers {
206 } 204 }
207 205
208 fn check(assist: AssistHandler, before: &str, expected: ExpectedResult) { 206 fn check(assist: AssistHandler, before: &str, expected: ExpectedResult) {
209 let (range_or_offset, before) = extract_range_or_offset(before); 207 let (text_without_caret, file_with_caret_id, range_or_offset, db) =
210 let range: TextRange = range_or_offset.into(); 208 if before.contains("//-") {
209 let (mut db, position) = RootDatabase::with_position(before);
210 db.set_local_roots(Arc::new(vec![db.file_source_root(position.file_id)]));
211 (
212 db.file_text(position.file_id).as_ref().to_owned(),
213 position.file_id,
214 RangeOrOffset::Offset(position.offset),
215 db,
216 )
217 } else {
218 let (range_or_offset, text_without_caret) = extract_range_or_offset(before);
219 let (db, file_id) = with_single_file(&text_without_caret);
220 (text_without_caret, file_id, range_or_offset, db)
221 };
222
223 let frange = FileRange { file_id: file_with_caret_id, range: range_or_offset.into() };
211 224
212 let (db, file_id) = with_single_file(&before);
213 let frange = FileRange { file_id, range };
214 let sema = Semantics::new(&db); 225 let sema = Semantics::new(&db);
215 let assist_ctx = AssistCtx::new(&sema, frange, true); 226 let assist_ctx = AssistCtx::new(&sema, frange, true);
216 227
@@ -218,7 +229,7 @@ mod helpers {
218 (Some(assist), ExpectedResult::After(after)) => { 229 (Some(assist), ExpectedResult::After(after)) => {
219 let action = assist.0[0].action.clone().unwrap(); 230 let action = assist.0[0].action.clone().unwrap();
220 231
221 let mut actual = action.edit.apply(&before); 232 let mut actual = action.edit.apply(&text_without_caret);
222 match action.cursor_position { 233 match action.cursor_position {
223 None => { 234 None => {
224 if let RangeOrOffset::Offset(before_cursor_pos) = range_or_offset { 235 if let RangeOrOffset::Offset(before_cursor_pos) = range_or_offset {
@@ -237,7 +248,7 @@ mod helpers {
237 (Some(assist), ExpectedResult::Target(target)) => { 248 (Some(assist), ExpectedResult::Target(target)) => {
238 let action = assist.0[0].action.clone().unwrap(); 249 let action = assist.0[0].action.clone().unwrap();
239 let range = action.target.expect("expected target on action"); 250 let range = action.target.expect("expected target on action");
240 assert_eq_text!(&before[range], target); 251 assert_eq_text!(&text_without_caret[range], target);
241 } 252 }
242 (Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"), 253 (Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"),
243 (None, ExpectedResult::After(_)) | (None, ExpectedResult::Target(_)) => { 254 (None, ExpectedResult::After(_)) | (None, ExpectedResult::Target(_)) => {
diff --git a/crates/ra_fmt/Cargo.toml b/crates/ra_fmt/Cargo.toml
index ea9befeaf..e9d057afc 100644
--- a/crates/ra_fmt/Cargo.toml
+++ b/crates/ra_fmt/Cargo.toml
@@ -9,6 +9,6 @@ publish = false
9doctest = false 9doctest = false
10 10
11[dependencies] 11[dependencies]
12itertools = "0.8.2" 12itertools = "0.9.0"
13 13
14ra_syntax = { path = "../ra_syntax" } 14ra_syntax = { path = "../ra_syntax" }
diff --git a/crates/ra_hir/Cargo.toml b/crates/ra_hir/Cargo.toml
index 42193b492..ba7b39a19 100644
--- a/crates/ra_hir/Cargo.toml
+++ b/crates/ra_hir/Cargo.toml
@@ -13,7 +13,7 @@ rustc-hash = "1.1.0"
13either = "1.5.3" 13either = "1.5.3"
14arrayvec = "0.5.1" 14arrayvec = "0.5.1"
15 15
16itertools = "0.8.2" 16itertools = "0.9.0"
17 17
18ra_syntax = { path = "../ra_syntax" } 18ra_syntax = { path = "../ra_syntax" }
19ra_db = { path = "../ra_db" } 19ra_db = { path = "../ra_db" }
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index e91abf6f5..c5cfd875f 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -33,7 +33,11 @@ use ra_syntax::{
33}; 33};
34use rustc_hash::FxHashSet; 34use rustc_hash::FxHashSet;
35 35
36use crate::{db::HirDatabase, has_source::HasSource, CallableDef, HirDisplay, InFile, Name}; 36use crate::{
37 db::{DefDatabase, HirDatabase},
38 has_source::HasSource,
39 CallableDef, HirDisplay, InFile, Name,
40};
37 41
38/// hir::Crate describes a single crate. It's the main interface with which 42/// hir::Crate describes a single crate. It's the main interface with which
39/// a crate's dependencies interact. Mostly, it should be just a proxy for the 43/// a crate's dependencies interact. Mostly, it should be just a proxy for the
@@ -274,20 +278,10 @@ impl Module {
274 /// this module, if possible. 278 /// this module, if possible.
275 pub fn find_use_path( 279 pub fn find_use_path(
276 self, 280 self,
277 db: &dyn HirDatabase, 281 db: &dyn DefDatabase,
278 item: ModuleDef, 282 item: impl Into<ItemInNs>,
279 ) -> Option<hir_def::path::ModPath> { 283 ) -> Option<hir_def::path::ModPath> {
280 // FIXME expose namespace choice 284 hir_def::find_path::find_path(db, item.into(), self.into())
281 hir_def::find_path::find_path(db.upcast(), determine_item_namespace(item), self.into())
282 }
283}
284
285fn determine_item_namespace(module_def: ModuleDef) -> ItemInNs {
286 match module_def {
287 ModuleDef::Static(_) | ModuleDef::Const(_) | ModuleDef::Function(_) => {
288 ItemInNs::Values(module_def.into())
289 }
290 _ => ItemInNs::Types(module_def.into()),
291 } 285 }
292} 286}
293 287
diff --git a/crates/ra_hir/src/from_id.rs b/crates/ra_hir/src/from_id.rs
index c179b13c6..62fb52e72 100644
--- a/crates/ra_hir/src/from_id.rs
+++ b/crates/ra_hir/src/from_id.rs
@@ -9,8 +9,8 @@ use hir_def::{
9}; 9};
10 10
11use crate::{ 11use crate::{
12 Adt, AssocItem, AttrDef, DefWithBody, EnumVariant, GenericDef, Local, ModuleDef, StructField, 12 code_model::ItemInNs, Adt, AssocItem, AttrDef, DefWithBody, EnumVariant, GenericDef, Local,
13 VariantDef, 13 MacroDef, ModuleDef, StructField, VariantDef,
14}; 14};
15 15
16macro_rules! from_id { 16macro_rules! from_id {
@@ -228,3 +228,20 @@ impl From<(DefWithBodyId, PatId)> for Local {
228 Local { parent, pat_id } 228 Local { parent, pat_id }
229 } 229 }
230} 230}
231
232impl From<MacroDef> for ItemInNs {
233 fn from(macro_def: MacroDef) -> Self {
234 ItemInNs::Macros(macro_def.into())
235 }
236}
237
238impl From<ModuleDef> for ItemInNs {
239 fn from(module_def: ModuleDef) -> Self {
240 match module_def {
241 ModuleDef::Static(_) | ModuleDef::Const(_) | ModuleDef::Function(_) => {
242 ItemInNs::Values(module_def.into())
243 }
244 _ => ItemInNs::Types(module_def.into()),
245 }
246 }
247}
diff --git a/crates/ra_ide/Cargo.toml b/crates/ra_ide/Cargo.toml
index 7235c944c..36eec0e60 100644
--- a/crates/ra_ide/Cargo.toml
+++ b/crates/ra_ide/Cargo.toml
@@ -14,7 +14,7 @@ wasm = []
14either = "1.5.3" 14either = "1.5.3"
15format-buf = "1.0.0" 15format-buf = "1.0.0"
16indexmap = "1.3.2" 16indexmap = "1.3.2"
17itertools = "0.8.2" 17itertools = "0.9.0"
18join_to_string = "0.1.3" 18join_to_string = "0.1.3"
19log = "0.4.8" 19log = "0.4.8"
20rustc-hash = "1.1.0" 20rustc-hash = "1.1.0"
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs
index 0ba382165..0a00054b2 100644
--- a/crates/ra_ide/src/completion/complete_postfix.rs
+++ b/crates/ra_ide/src/completion/complete_postfix.rs
@@ -1,6 +1,9 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use ra_syntax::{ast::AstNode, TextRange, TextUnit}; 3use ra_syntax::{
4 ast::{self, AstNode},
5 TextRange, TextUnit,
6};
4use ra_text_edit::TextEdit; 7use ra_text_edit::TextEdit;
5 8
6use crate::{ 9use crate::{
@@ -21,13 +24,8 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
21 None => return, 24 None => return,
22 }; 25 };
23 26
24 let receiver_text = if ctx.dot_receiver_is_ambiguous_float_literal { 27 let receiver_text =
25 let text = dot_receiver.syntax().text(); 28 get_receiver_text(dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);
26 let without_dot = ..text.len() - TextUnit::of_char('.');
27 text.slice(without_dot).to_string()
28 } else {
29 dot_receiver.syntax().text().to_string()
30 };
31 29
32 let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) { 30 let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) {
33 Some(it) => it, 31 Some(it) => it,
@@ -35,10 +33,17 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
35 }; 33 };
36 34
37 if receiver_ty.is_bool() || receiver_ty.is_unknown() { 35 if receiver_ty.is_bool() || receiver_ty.is_unknown() {
38 postfix_snippet(ctx, "if", "if expr {}", &format!("if {} {{$0}}", receiver_text))
39 .add_to(acc);
40 postfix_snippet( 36 postfix_snippet(
41 ctx, 37 ctx,
38 &dot_receiver,
39 "if",
40 "if expr {}",
41 &format!("if {} {{$0}}", receiver_text),
42 )
43 .add_to(acc);
44 postfix_snippet(
45 ctx,
46 &dot_receiver,
42 "while", 47 "while",
43 "while expr {}", 48 "while expr {}",
44 &format!("while {} {{\n$0\n}}", receiver_text), 49 &format!("while {} {{\n$0\n}}", receiver_text),
@@ -46,28 +51,70 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
46 .add_to(acc); 51 .add_to(acc);
47 } 52 }
48 53
49 postfix_snippet(ctx, "not", "!expr", &format!("!{}", receiver_text)).add_to(acc); 54 // !&&&42 is a compiler error, ergo process it before considering the references
55 postfix_snippet(ctx, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text)).add_to(acc);
50 56
51 postfix_snippet(ctx, "ref", "&expr", &format!("&{}", receiver_text)).add_to(acc); 57 postfix_snippet(ctx, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text)).add_to(acc);
52 postfix_snippet(ctx, "refm", "&mut expr", &format!("&mut {}", receiver_text)).add_to(acc); 58 postfix_snippet(ctx, &dot_receiver, "refm", "&mut expr", &format!("&mut {}", receiver_text))
59 .add_to(acc);
60
61 // The rest of the postfix completions create an expression that moves an argument,
62 // so it's better to consider references now to avoid breaking the compilation
63 let dot_receiver = include_references(dot_receiver);
64 let receiver_text =
65 get_receiver_text(&dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);
53 66
54 postfix_snippet( 67 postfix_snippet(
55 ctx, 68 ctx,
69 &dot_receiver,
56 "match", 70 "match",
57 "match expr {}", 71 "match expr {}",
58 &format!("match {} {{\n ${{1:_}} => {{$0\\}},\n}}", receiver_text), 72 &format!("match {} {{\n ${{1:_}} => {{$0\\}},\n}}", receiver_text),
59 ) 73 )
60 .add_to(acc); 74 .add_to(acc);
61 75
62 postfix_snippet(ctx, "dbg", "dbg!(expr)", &format!("dbg!({})", receiver_text)).add_to(acc); 76 postfix_snippet(
77 ctx,
78 &dot_receiver,
79 "box",
80 "Box::new(expr)",
81 &format!("Box::new({})", receiver_text),
82 )
83 .add_to(acc);
63 84
64 postfix_snippet(ctx, "box", "Box::new(expr)", &format!("Box::new({})", receiver_text)) 85 postfix_snippet(ctx, &dot_receiver, "dbg", "dbg!(expr)", &format!("dbg!({})", receiver_text))
65 .add_to(acc); 86 .add_to(acc);
66} 87}
67 88
68fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder { 89fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String {
90 if receiver_is_ambiguous_float_literal {
91 let text = receiver.syntax().text();
92 let without_dot = ..text.len() - TextUnit::of_char('.');
93 text.slice(without_dot).to_string()
94 } else {
95 receiver.to_string()
96 }
97}
98
99fn include_references(initial_element: &ast::Expr) -> ast::Expr {
100 let mut resulting_element = initial_element.clone();
101 while let Some(parent_ref_element) =
102 resulting_element.syntax().parent().and_then(ast::RefExpr::cast)
103 {
104 resulting_element = ast::Expr::from(parent_ref_element);
105 }
106 resulting_element
107}
108
109fn postfix_snippet(
110 ctx: &CompletionContext,
111 receiver: &ast::Expr,
112 label: &str,
113 detail: &str,
114 snippet: &str,
115) -> Builder {
69 let edit = { 116 let edit = {
70 let receiver_syntax = ctx.dot_receiver.as_ref().expect("no receiver available").syntax(); 117 let receiver_syntax = receiver.syntax();
71 let receiver_range = ctx.sema.original_range(receiver_syntax).range; 118 let receiver_range = ctx.sema.original_range(receiver_syntax).range;
72 let delete_range = TextRange::from_to(receiver_range.start(), ctx.source_range().end()); 119 let delete_range = TextRange::from_to(receiver_range.start(), ctx.source_range().end());
73 TextEdit::replace(delete_range, snippet.to_string()) 120 TextEdit::replace(delete_range, snippet.to_string())
@@ -340,4 +387,63 @@ mod tests {
340 "### 387 "###
341 ); 388 );
342 } 389 }
390
391 #[test]
392 fn postfix_completion_for_references() {
393 assert_debug_snapshot!(
394 do_postfix_completion(
395 r#"
396 fn main() {
397 &&&&42.<|>
398 }
399 "#,
400 ),
401 @r###"
402 [
403 CompletionItem {
404 label: "box",
405 source_range: [56; 56),
406 delete: [49; 56),
407 insert: "Box::new(&&&&42)",
408 detail: "Box::new(expr)",
409 },
410 CompletionItem {
411 label: "dbg",
412 source_range: [56; 56),
413 delete: [49; 56),
414 insert: "dbg!(&&&&42)",
415 detail: "dbg!(expr)",
416 },
417 CompletionItem {
418 label: "match",
419 source_range: [56; 56),
420 delete: [49; 56),
421 insert: "match &&&&42 {\n ${1:_} => {$0\\},\n}",
422 detail: "match expr {}",
423 },
424 CompletionItem {
425 label: "not",
426 source_range: [56; 56),
427 delete: [53; 56),
428 insert: "!42",
429 detail: "!expr",
430 },
431 CompletionItem {
432 label: "ref",
433 source_range: [56; 56),
434 delete: [53; 56),
435 insert: "&42",
436 detail: "&expr",
437 },
438 CompletionItem {
439 label: "refm",
440 source_range: [56; 56),
441 delete: [53; 56),
442 insert: "&mut 42",
443 detail: "&mut expr",
444 },
445 ]
446 "###
447 );
448 }
343} 449}
diff --git a/crates/ra_ide_db/Cargo.toml b/crates/ra_ide_db/Cargo.toml
index de4f5bce0..c3921bd40 100644
--- a/crates/ra_ide_db/Cargo.toml
+++ b/crates/ra_ide_db/Cargo.toml
@@ -17,6 +17,7 @@ fst = { version = "0.4", default-features = false }
17rustc-hash = "1.1.0" 17rustc-hash = "1.1.0"
18superslice = "1.0.0" 18superslice = "1.0.0"
19once_cell = "1.3.1" 19once_cell = "1.3.1"
20either = "1.5.3"
20 21
21ra_syntax = { path = "../ra_syntax" } 22ra_syntax = { path = "../ra_syntax" }
22ra_text_edit = { path = "../ra_text_edit" } 23ra_text_edit = { path = "../ra_text_edit" }
diff --git a/crates/ra_ide_db/src/imports_locator.rs b/crates/ra_ide_db/src/imports_locator.rs
index c96351982..bf0d8db60 100644
--- a/crates/ra_ide_db/src/imports_locator.rs
+++ b/crates/ra_ide_db/src/imports_locator.rs
@@ -1,7 +1,7 @@
1//! This module contains an import search funcionality that is provided to the ra_assists module. 1//! This module contains an import search funcionality that is provided to the ra_assists module.
2//! Later, this should be moved away to a separate crate that is accessible from the ra_assists module. 2//! Later, this should be moved away to a separate crate that is accessible from the ra_assists module.
3 3
4use hir::{ModuleDef, Semantics}; 4use hir::{MacroDef, ModuleDef, Semantics};
5use ra_prof::profile; 5use ra_prof::profile;
6use ra_syntax::{ast, AstNode, SyntaxKind::NAME}; 6use ra_syntax::{ast, AstNode, SyntaxKind::NAME};
7 7
@@ -10,6 +10,7 @@ use crate::{
10 symbol_index::{self, FileSymbol, Query}, 10 symbol_index::{self, FileSymbol, Query},
11 RootDatabase, 11 RootDatabase,
12}; 12};
13use either::Either;
13 14
14pub struct ImportsLocator<'a> { 15pub struct ImportsLocator<'a> {
15 sema: Semantics<'a, RootDatabase>, 16 sema: Semantics<'a, RootDatabase>,
@@ -20,7 +21,7 @@ impl<'a> ImportsLocator<'a> {
20 Self { sema: Semantics::new(db) } 21 Self { sema: Semantics::new(db) }
21 } 22 }
22 23
23 pub fn find_imports(&mut self, name_to_import: &str) -> Vec<ModuleDef> { 24 pub fn find_imports(&mut self, name_to_import: &str) -> Vec<Either<ModuleDef, MacroDef>> {
24 let _p = profile("search_for_imports"); 25 let _p = profile("search_for_imports");
25 let db = self.sema.db; 26 let db = self.sema.db;
26 27
@@ -43,7 +44,8 @@ impl<'a> ImportsLocator<'a> {
43 .chain(lib_results.into_iter()) 44 .chain(lib_results.into_iter())
44 .filter_map(|import_candidate| self.get_name_definition(&import_candidate)) 45 .filter_map(|import_candidate| self.get_name_definition(&import_candidate))
45 .filter_map(|name_definition_to_import| match name_definition_to_import { 46 .filter_map(|name_definition_to_import| match name_definition_to_import {
46 Definition::ModuleDef(module_def) => Some(module_def), 47 Definition::ModuleDef(module_def) => Some(Either::Left(module_def)),
48 Definition::Macro(macro_def) => Some(Either::Right(macro_def)),
47 _ => None, 49 _ => None,
48 }) 50 })
49 .collect() 51 .collect()
diff --git a/crates/ra_syntax/Cargo.toml b/crates/ra_syntax/Cargo.toml
index 8efc6b368..6fccc2303 100644
--- a/crates/ra_syntax/Cargo.toml
+++ b/crates/ra_syntax/Cargo.toml
@@ -11,7 +11,7 @@ repository = "https://github.com/rust-analyzer/rust-analyzer"
11doctest = false 11doctest = false
12 12
13[dependencies] 13[dependencies]
14itertools = "0.8.2" 14itertools = "0.9.0"
15rowan = "0.9.1" 15rowan = "0.9.1"
16rustc_lexer = "0.1.0" 16rustc_lexer = "0.1.0"
17rustc-hash = "1.1.0" 17rustc-hash = "1.1.0"
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index d44f0ef1d..e071e9b8d 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -17,7 +17,7 @@ anyhow = "1.0.26"
17crossbeam-channel = "0.4.0" 17crossbeam-channel = "0.4.0"
18env_logger = { version = "0.7.1", default-features = false } 18env_logger = { version = "0.7.1", default-features = false }
19globset = "0.4.4" 19globset = "0.4.4"
20itertools = "0.8.2" 20itertools = "0.9.0"
21jod-thread = "0.1.0" 21jod-thread = "0.1.0"
22log = "0.4.8" 22log = "0.4.8"
23lsp-types = { version = "0.73.0", features = ["proposed"] } 23lsp-types = { version = "0.73.0", features = ["proposed"] }
diff --git a/editors/code/package.json b/editors/code/package.json
index eb5748515..1d113ebb6 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -73,10 +73,18 @@
73 "type": "string" 73 "type": "string"
74 }, 74 },
75 "args": { 75 "args": {
76 "type": "array" 76 "type": "array",
77 "items": {
78 "type": "string"
79 }
77 }, 80 },
78 "env": { 81 "env": {
79 "type": "object" 82 "type": "object",
83 "patternProperties": {
84 ".+": {
85 "type": "string"
86 }
87 }
80 } 88 }
81 } 89 }
82 } 90 }
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index 08d821dd0..82ca749f3 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -99,8 +99,10 @@ export async function createClient(config: Config, serverPath: string): Promise<
99 // Note that while the CallHierarchyFeature is stable the LSP protocol is not. 99 // Note that while the CallHierarchyFeature is stable the LSP protocol is not.
100 res.registerFeature(new CallHierarchyFeature(res)); 100 res.registerFeature(new CallHierarchyFeature(res));
101 101
102 if (config.highlightingSemanticTokens) { 102 if (config.package.enableProposedApi) {
103 res.registerFeature(new SemanticTokensFeature(res)); 103 if (config.highlightingSemanticTokens) {
104 res.registerFeature(new SemanticTokensFeature(res));
105 }
104 } 106 }
105 107
106 return res; 108 return res;
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index 16ac50a0a..7668c20b7 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -19,17 +19,11 @@ export class Config {
19 ] 19 ]
20 .map(opt => `${this.rootSection}.${opt}`); 20 .map(opt => `${this.rootSection}.${opt}`);
21 21
22 readonly packageJsonVersion: string = vscode 22 readonly package: {
23 .extensions 23 version: string;
24 .getExtension(this.extensionId)! 24 releaseTag: string | undefined;
25 .packageJSON 25 enableProposedApi: boolean | undefined;
26 .version; 26 } = vscode.extensions.getExtension(this.extensionId)!.packageJSON;
27
28 readonly releaseTag: string | undefined = vscode
29 .extensions
30 .getExtension(this.extensionId)!
31 .packageJSON
32 .releaseTag ?? undefined;
33 27
34 readonly globalStoragePath: string; 28 readonly globalStoragePath: string;
35 29
@@ -42,7 +36,7 @@ export class Config {
42 private refreshLogging() { 36 private refreshLogging() {
43 log.setEnabled(this.traceExtension); 37 log.setEnabled(this.traceExtension);
44 log.debug( 38 log.debug(
45 "Extension version:", this.packageJsonVersion, 39 "Extension version:", this.package.version,
46 "using configuration:", this.cfg 40 "using configuration:", this.cfg
47 ); 41 );
48 } 42 }
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 5d2da9a76..7b7c19dfc 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -110,9 +110,9 @@ async function bootstrap(config: Config, state: PersistentState): Promise<string
110} 110}
111 111
112async function bootstrapExtension(config: Config, state: PersistentState): Promise<void> { 112async function bootstrapExtension(config: Config, state: PersistentState): Promise<void> {
113 if (config.releaseTag === undefined) return; 113 if (config.package.releaseTag === undefined) return;
114 if (config.channel === "stable") { 114 if (config.channel === "stable") {
115 if (config.releaseTag === NIGHTLY_TAG) { 115 if (config.package.releaseTag === NIGHTLY_TAG) {
116 vscode.window.showWarningMessage(`You are running a nightly version of rust-analyzer extension. 116 vscode.window.showWarningMessage(`You are running a nightly version of rust-analyzer extension.
117To switch to stable, uninstall the extension and re-install it from the marketplace`); 117To switch to stable, uninstall the extension and re-install it from the marketplace`);
118 } 118 }
@@ -185,7 +185,7 @@ async function getServer(config: Config, state: PersistentState): Promise<string
185 } 185 }
186 return explicitPath; 186 return explicitPath;
187 }; 187 };
188 if (config.releaseTag === undefined) return "rust-analyzer"; 188 if (config.package.releaseTag === undefined) return "rust-analyzer";
189 189
190 let binaryName: string | undefined = undefined; 190 let binaryName: string | undefined = undefined;
191 if (process.arch === "x64" || process.arch === "x32") { 191 if (process.arch === "x64" || process.arch === "x32") {
@@ -211,21 +211,21 @@ async function getServer(config: Config, state: PersistentState): Promise<string
211 await state.updateServerVersion(undefined); 211 await state.updateServerVersion(undefined);
212 } 212 }
213 213
214 if (state.serverVersion === config.packageJsonVersion) return dest; 214 if (state.serverVersion === config.package.version) return dest;
215 215
216 if (config.askBeforeDownload) { 216 if (config.askBeforeDownload) {
217 const userResponse = await vscode.window.showInformationMessage( 217 const userResponse = await vscode.window.showInformationMessage(
218 `Language server version ${config.packageJsonVersion} for rust-analyzer is not installed.`, 218 `Language server version ${config.package.version} for rust-analyzer is not installed.`,
219 "Download now" 219 "Download now"
220 ); 220 );
221 if (userResponse !== "Download now") return dest; 221 if (userResponse !== "Download now") return dest;
222 } 222 }
223 223
224 const release = await fetchRelease(config.releaseTag); 224 const release = await fetchRelease(config.package.releaseTag);
225 const artifact = release.assets.find(artifact => artifact.name === binaryName); 225 const artifact = release.assets.find(artifact => artifact.name === binaryName);
226 assert(!!artifact, `Bad release: ${JSON.stringify(release)}`); 226 assert(!!artifact, `Bad release: ${JSON.stringify(release)}`);
227 227
228 await download(artifact.browser_download_url, dest, "Downloading rust-analyzer server", { mode: 0o755 }); 228 await download(artifact.browser_download_url, dest, "Downloading rust-analyzer server", { mode: 0o755 });
229 await state.updateServerVersion(config.packageJsonVersion); 229 await state.updateServerVersion(config.package.version);
230 return dest; 230 return dest;
231} 231}