aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/base_db/src/fixture.rs10
-rw-r--r--crates/hir_def/src/nameres/tests/incremental.rs74
-rw-r--r--crates/hir_ty/src/tests.rs45
-rw-r--r--crates/hir_ty/src/tests/incremental.rs51
-rw-r--r--crates/ide/src/annotations.rs34
-rw-r--r--crates/ide/src/fixture.rs17
-rwxr-xr-xcrates/ide/src/folding_ranges.rs17
-rw-r--r--crates/ide_completion/src/completions/keyword.rs197
-rw-r--r--crates/ide_completion/src/completions/unqualified_path.rs30
-rw-r--r--crates/ide_completion/src/context.rs53
-rw-r--r--crates/ide_completion/src/patterns.rs276
-rw-r--r--crates/ide_completion/src/render/macro_.rs6
-rw-r--r--crates/ide_completion/src/test_utils.rs16
-rw-r--r--crates/ide_db/src/call_info/tests.rs6
-rw-r--r--crates/ide_db/src/traits/tests.rs6
-rw-r--r--crates/rust-analyzer/src/bin/main.rs84
-rw-r--r--crates/rust-analyzer/src/caps.rs20
-rw-r--r--crates/rust-analyzer/src/config.rs2
-rw-r--r--crates/rust-analyzer/src/to_proto.rs1
-rw-r--r--crates/test_utils/src/lib.rs15
20 files changed, 587 insertions, 373 deletions
diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs
index 0132565e4..69ceba735 100644
--- a/crates/base_db/src/fixture.rs
+++ b/crates/base_db/src/fixture.rs
@@ -34,19 +34,13 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
34 34
35 fn with_position(ra_fixture: &str) -> (Self, FilePosition) { 35 fn with_position(ra_fixture: &str) -> (Self, FilePosition) {
36 let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); 36 let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture);
37 let offset = match range_or_offset { 37 let offset = range_or_offset.expect_offset();
38 RangeOrOffset::Range(_) => panic!("Expected a cursor position, got a range instead"),
39 RangeOrOffset::Offset(it) => it,
40 };
41 (db, FilePosition { file_id, offset }) 38 (db, FilePosition { file_id, offset })
42 } 39 }
43 40
44 fn with_range(ra_fixture: &str) -> (Self, FileRange) { 41 fn with_range(ra_fixture: &str) -> (Self, FileRange) {
45 let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); 42 let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture);
46 let range = match range_or_offset { 43 let range = range_or_offset.expect_range();
47 RangeOrOffset::Range(it) => it,
48 RangeOrOffset::Offset(_) => panic!("Expected a cursor range, got a position instead"),
49 };
50 (db, FileRange { file_id, range }) 44 (db, FileRange { file_id, range })
51 } 45 }
52 46
diff --git a/crates/hir_def/src/nameres/tests/incremental.rs b/crates/hir_def/src/nameres/tests/incremental.rs
index d884a6eb4..7bf152e26 100644
--- a/crates/hir_def/src/nameres/tests/incremental.rs
+++ b/crates/hir_def/src/nameres/tests/incremental.rs
@@ -1,6 +1,8 @@
1use std::sync::Arc; 1use std::sync::Arc;
2 2
3use base_db::SourceDatabaseExt; 3use base_db::{salsa::SweepStrategy, SourceDatabaseExt};
4
5use crate::{AdtId, ModuleDefId};
4 6
5use super::*; 7use super::*;
6 8
@@ -163,3 +165,73 @@ m!(Z);
163 assert_eq!(n_reparsed_macros, 0); 165 assert_eq!(n_reparsed_macros, 0);
164 } 166 }
165} 167}
168
169#[test]
170fn item_tree_prevents_reparsing() {
171 // The `ItemTree` is used by both name resolution and the various queries in `adt.rs` and
172 // `data.rs`. After computing the `ItemTree` and deleting the parse tree, we should be able to
173 // run those other queries without triggering a reparse.
174
175 let (db, pos) = TestDB::with_position(
176 r#"
177pub struct S;
178pub union U {}
179pub enum E {
180 Variant,
181}
182pub fn f(_: S) { $0 }
183pub trait Tr {}
184impl Tr for () {}
185pub const C: u8 = 0;
186pub static ST: u8 = 0;
187pub type Ty = ();
188"#,
189 );
190 let krate = db.test_crate();
191 {
192 let events = db.log_executed(|| {
193 db.file_item_tree(pos.file_id.into());
194 });
195 let n_calculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count();
196 assert_eq!(n_calculated_item_trees, 1);
197 let n_parsed_files = events.iter().filter(|it| it.contains("parse(")).count();
198 assert_eq!(n_parsed_files, 1);
199 }
200
201 // Delete the parse tree.
202 let sweep = SweepStrategy::default().discard_values().sweep_all_revisions();
203 base_db::ParseQuery.in_db(&db).sweep(sweep);
204
205 {
206 let events = db.log_executed(|| {
207 let crate_def_map = db.crate_def_map(krate);
208 let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
209 assert_eq!(module_data.scope.resolutions().count(), 8);
210 assert_eq!(module_data.scope.impls().count(), 1);
211
212 for imp in module_data.scope.impls() {
213 db.impl_data(imp);
214 }
215
216 for (_, res) in module_data.scope.resolutions() {
217 match res.values.or(res.types).unwrap().0 {
218 ModuleDefId::FunctionId(f) => drop(db.function_data(f)),
219 ModuleDefId::AdtId(adt) => match adt {
220 AdtId::StructId(it) => drop(db.struct_data(it)),
221 AdtId::UnionId(it) => drop(db.union_data(it)),
222 AdtId::EnumId(it) => drop(db.enum_data(it)),
223 },
224 ModuleDefId::ConstId(it) => drop(db.const_data(it)),
225 ModuleDefId::StaticId(it) => drop(db.static_data(it)),
226 ModuleDefId::TraitId(it) => drop(db.trait_data(it)),
227 ModuleDefId::TypeAliasId(it) => drop(db.type_alias_data(it)),
228 ModuleDefId::EnumVariantId(_)
229 | ModuleDefId::ModuleId(_)
230 | ModuleDefId::BuiltinType(_) => unreachable!(),
231 }
232 }
233 });
234 let n_reparsed_files = events.iter().filter(|it| it.contains("parse(")).count();
235 assert_eq!(n_reparsed_files, 0);
236 }
237}
diff --git a/crates/hir_ty/src/tests.rs b/crates/hir_ty/src/tests.rs
index cc819373c..9d726b024 100644
--- a/crates/hir_ty/src/tests.rs
+++ b/crates/hir_ty/src/tests.rs
@@ -7,6 +7,7 @@ mod traits;
7mod method_resolution; 7mod method_resolution;
8mod macros; 8mod macros;
9mod display_source_code; 9mod display_source_code;
10mod incremental;
10 11
11use std::{env, sync::Arc}; 12use std::{env, sync::Arc};
12 13
@@ -317,50 +318,6 @@ fn ellipsize(mut text: String, max_len: usize) -> String {
317 text 318 text
318} 319}
319 320
320#[test]
321fn typing_whitespace_inside_a_function_should_not_invalidate_types() {
322 let (mut db, pos) = TestDB::with_position(
323 "
324 //- /lib.rs
325 fn foo() -> i32 {
326 $01 + 1
327 }
328 ",
329 );
330 {
331 let events = db.log_executed(|| {
332 let module = db.module_for_file(pos.file_id);
333 let crate_def_map = module.def_map(&db);
334 visit_module(&db, &crate_def_map, module.local_id, &mut |def| {
335 db.infer(def);
336 });
337 });
338 assert!(format!("{:?}", events).contains("infer"))
339 }
340
341 let new_text = "
342 fn foo() -> i32 {
343 1
344 +
345 1
346 }
347 "
348 .to_string();
349
350 db.set_file_text(pos.file_id, Arc::new(new_text));
351
352 {
353 let events = db.log_executed(|| {
354 let module = db.module_for_file(pos.file_id);
355 let crate_def_map = module.def_map(&db);
356 visit_module(&db, &crate_def_map, module.local_id, &mut |def| {
357 db.infer(def);
358 });
359 });
360 assert!(!format!("{:?}", events).contains("infer"), "{:#?}", events)
361 }
362}
363
364fn check_infer(ra_fixture: &str, expect: Expect) { 321fn check_infer(ra_fixture: &str, expect: Expect) {
365 let mut actual = infer(ra_fixture); 322 let mut actual = infer(ra_fixture);
366 actual.push('\n'); 323 actual.push('\n');
diff --git a/crates/hir_ty/src/tests/incremental.rs b/crates/hir_ty/src/tests/incremental.rs
new file mode 100644
index 000000000..3e08e83e8
--- /dev/null
+++ b/crates/hir_ty/src/tests/incremental.rs
@@ -0,0 +1,51 @@
1use std::sync::Arc;
2
3use base_db::{fixture::WithFixture, SourceDatabaseExt};
4
5use crate::{db::HirDatabase, test_db::TestDB};
6
7use super::visit_module;
8
9#[test]
10fn typing_whitespace_inside_a_function_should_not_invalidate_types() {
11 let (mut db, pos) = TestDB::with_position(
12 "
13 //- /lib.rs
14 fn foo() -> i32 {
15 $01 + 1
16 }
17 ",
18 );
19 {
20 let events = db.log_executed(|| {
21 let module = db.module_for_file(pos.file_id);
22 let crate_def_map = module.def_map(&db);
23 visit_module(&db, &crate_def_map, module.local_id, &mut |def| {
24 db.infer(def);
25 });
26 });
27 assert!(format!("{:?}", events).contains("infer"))
28 }
29
30 let new_text = "
31 fn foo() -> i32 {
32 1
33 +
34 1
35 }
36 "
37 .to_string();
38
39 db.set_file_text(pos.file_id, Arc::new(new_text));
40
41 {
42 let events = db.log_executed(|| {
43 let module = db.module_for_file(pos.file_id);
44 let crate_def_map = module.def_map(&db);
45 visit_module(&db, &crate_def_map, module.local_id, &mut |def| {
46 db.infer(def);
47 });
48 });
49 assert!(!format!("{:?}", events).contains("infer"), "{:#?}", events)
50 }
51}
diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs
index b0c4ed60a..8d68dce05 100644
--- a/crates/ide/src/annotations.rs
+++ b/crates/ide/src/annotations.rs
@@ -58,7 +58,7 @@ pub(crate) fn annotations(
58 } 58 }
59 59
60 let action = runnable.action(); 60 let action = runnable.action();
61 let range = runnable.nav.full_range; 61 let range = runnable.nav.focus_or_full_range();
62 62
63 if config.run { 63 if config.run {
64 annotations.push(Annotation { 64 annotations.push(Annotation {
@@ -224,7 +224,7 @@ fn main() {
224 expect![[r#" 224 expect![[r#"
225 [ 225 [
226 Annotation { 226 Annotation {
227 range: 50..85, 227 range: 53..57,
228 kind: Runnable { 228 kind: Runnable {
229 debug: false, 229 debug: false,
230 runnable: Runnable { 230 runnable: Runnable {
@@ -243,7 +243,7 @@ fn main() {
243 }, 243 },
244 }, 244 },
245 Annotation { 245 Annotation {
246 range: 50..85, 246 range: 53..57,
247 kind: Runnable { 247 kind: Runnable {
248 debug: true, 248 debug: true,
249 runnable: Runnable { 249 runnable: Runnable {
@@ -328,7 +328,7 @@ fn main() {
328 expect![[r#" 328 expect![[r#"
329 [ 329 [
330 Annotation { 330 Annotation {
331 range: 14..48, 331 range: 17..21,
332 kind: Runnable { 332 kind: Runnable {
333 debug: false, 333 debug: false,
334 runnable: Runnable { 334 runnable: Runnable {
@@ -347,7 +347,7 @@ fn main() {
347 }, 347 },
348 }, 348 },
349 Annotation { 349 Annotation {
350 range: 14..48, 350 range: 17..21,
351 kind: Runnable { 351 kind: Runnable {
352 debug: true, 352 debug: true,
353 runnable: Runnable { 353 runnable: Runnable {
@@ -436,7 +436,7 @@ fn main() {
436 expect![[r#" 436 expect![[r#"
437 [ 437 [
438 Annotation { 438 Annotation {
439 range: 66..100, 439 range: 69..73,
440 kind: Runnable { 440 kind: Runnable {
441 debug: false, 441 debug: false,
442 runnable: Runnable { 442 runnable: Runnable {
@@ -455,7 +455,7 @@ fn main() {
455 }, 455 },
456 }, 456 },
457 Annotation { 457 Annotation {
458 range: 66..100, 458 range: 69..73,
459 kind: Runnable { 459 kind: Runnable {
460 debug: true, 460 debug: true,
461 runnable: Runnable { 461 runnable: Runnable {
@@ -597,7 +597,7 @@ fn main() {}
597 expect![[r#" 597 expect![[r#"
598 [ 598 [
599 Annotation { 599 Annotation {
600 range: 0..12, 600 range: 3..7,
601 kind: Runnable { 601 kind: Runnable {
602 debug: false, 602 debug: false,
603 runnable: Runnable { 603 runnable: Runnable {
@@ -616,7 +616,7 @@ fn main() {}
616 }, 616 },
617 }, 617 },
618 Annotation { 618 Annotation {
619 range: 0..12, 619 range: 3..7,
620 kind: Runnable { 620 kind: Runnable {
621 debug: true, 621 debug: true,
622 runnable: Runnable { 622 runnable: Runnable {
@@ -670,7 +670,7 @@ fn main() {
670 expect![[r#" 670 expect![[r#"
671 [ 671 [
672 Annotation { 672 Annotation {
673 range: 58..95, 673 range: 61..65,
674 kind: Runnable { 674 kind: Runnable {
675 debug: false, 675 debug: false,
676 runnable: Runnable { 676 runnable: Runnable {
@@ -689,7 +689,7 @@ fn main() {
689 }, 689 },
690 }, 690 },
691 Annotation { 691 Annotation {
692 range: 58..95, 692 range: 61..65,
693 kind: Runnable { 693 kind: Runnable {
694 debug: true, 694 debug: true,
695 runnable: Runnable { 695 runnable: Runnable {
@@ -812,7 +812,7 @@ mod tests {
812 expect![[r#" 812 expect![[r#"
813 [ 813 [
814 Annotation { 814 Annotation {
815 range: 0..12, 815 range: 3..7,
816 kind: Runnable { 816 kind: Runnable {
817 debug: false, 817 debug: false,
818 runnable: Runnable { 818 runnable: Runnable {
@@ -831,7 +831,7 @@ mod tests {
831 }, 831 },
832 }, 832 },
833 Annotation { 833 Annotation {
834 range: 0..12, 834 range: 3..7,
835 kind: Runnable { 835 kind: Runnable {
836 debug: true, 836 debug: true,
837 runnable: Runnable { 837 runnable: Runnable {
@@ -850,7 +850,7 @@ mod tests {
850 }, 850 },
851 }, 851 },
852 Annotation { 852 Annotation {
853 range: 14..64, 853 range: 18..23,
854 kind: Runnable { 854 kind: Runnable {
855 debug: false, 855 debug: false,
856 runnable: Runnable { 856 runnable: Runnable {
@@ -871,7 +871,7 @@ mod tests {
871 }, 871 },
872 }, 872 },
873 Annotation { 873 Annotation {
874 range: 14..64, 874 range: 18..23,
875 kind: Runnable { 875 kind: Runnable {
876 debug: true, 876 debug: true,
877 runnable: Runnable { 877 runnable: Runnable {
@@ -892,7 +892,7 @@ mod tests {
892 }, 892 },
893 }, 893 },
894 Annotation { 894 Annotation {
895 range: 30..62, 895 range: 45..57,
896 kind: Runnable { 896 kind: Runnable {
897 debug: false, 897 debug: false,
898 runnable: Runnable { 898 runnable: Runnable {
@@ -918,7 +918,7 @@ mod tests {
918 }, 918 },
919 }, 919 },
920 Annotation { 920 Annotation {
921 range: 30..62, 921 range: 45..57,
922 kind: Runnable { 922 kind: Runnable {
923 debug: true, 923 debug: true,
924 runnable: Runnable { 924 runnable: Runnable {
diff --git a/crates/ide/src/fixture.rs b/crates/ide/src/fixture.rs
index cc6641ba1..6780af617 100644
--- a/crates/ide/src/fixture.rs
+++ b/crates/ide/src/fixture.rs
@@ -1,7 +1,7 @@
1//! Utilities for creating `Analysis` instances for tests. 1//! Utilities for creating `Analysis` instances for tests.
2use ide_db::base_db::fixture::ChangeFixture; 2use ide_db::base_db::fixture::ChangeFixture;
3use syntax::{TextRange, TextSize}; 3use syntax::{TextRange, TextSize};
4use test_utils::{extract_annotations, RangeOrOffset}; 4use test_utils::extract_annotations;
5 5
6use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange}; 6use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange};
7 7
@@ -27,10 +27,7 @@ pub(crate) fn position(ra_fixture: &str) -> (Analysis, FilePosition) {
27 let change_fixture = ChangeFixture::parse(ra_fixture); 27 let change_fixture = ChangeFixture::parse(ra_fixture);
28 host.db.apply_change(change_fixture.change); 28 host.db.apply_change(change_fixture.change);
29 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); 29 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
30 let offset = match range_or_offset { 30 let offset = range_or_offset.expect_offset();
31 RangeOrOffset::Range(_) => panic!(),
32 RangeOrOffset::Offset(it) => it,
33 };
34 (host.analysis(), FilePosition { file_id, offset }) 31 (host.analysis(), FilePosition { file_id, offset })
35} 32}
36 33
@@ -40,10 +37,7 @@ pub(crate) fn range(ra_fixture: &str) -> (Analysis, FileRange) {
40 let change_fixture = ChangeFixture::parse(ra_fixture); 37 let change_fixture = ChangeFixture::parse(ra_fixture);
41 host.db.apply_change(change_fixture.change); 38 host.db.apply_change(change_fixture.change);
42 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); 39 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
43 let range = match range_or_offset { 40 let range = range_or_offset.expect_range();
44 RangeOrOffset::Range(it) => it,
45 RangeOrOffset::Offset(_) => panic!(),
46 };
47 (host.analysis(), FileRange { file_id, range }) 41 (host.analysis(), FileRange { file_id, range })
48} 42}
49 43
@@ -53,10 +47,7 @@ pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(Fil
53 let change_fixture = ChangeFixture::parse(ra_fixture); 47 let change_fixture = ChangeFixture::parse(ra_fixture);
54 host.db.apply_change(change_fixture.change); 48 host.db.apply_change(change_fixture.change);
55 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); 49 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
56 let offset = match range_or_offset { 50 let offset = range_or_offset.expect_offset();
57 RangeOrOffset::Range(_) => panic!(),
58 RangeOrOffset::Offset(it) => it,
59 };
60 51
61 let annotations = change_fixture 52 let annotations = change_fixture
62 .files 53 .files
diff --git a/crates/ide/src/folding_ranges.rs b/crates/ide/src/folding_ranges.rs
index b893c1c54..c5015a345 100755
--- a/crates/ide/src/folding_ranges.rs
+++ b/crates/ide/src/folding_ranges.rs
@@ -19,6 +19,7 @@ pub enum FoldKind {
19 Statics, 19 Statics,
20 Array, 20 Array,
21 WhereClause, 21 WhereClause,
22 ReturnType,
22} 23}
23 24
24#[derive(Debug)] 25#[derive(Debug)]
@@ -131,6 +132,7 @@ fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> {
131 COMMENT => Some(FoldKind::Comment), 132 COMMENT => Some(FoldKind::Comment),
132 ARG_LIST | PARAM_LIST => Some(FoldKind::ArgList), 133 ARG_LIST | PARAM_LIST => Some(FoldKind::ArgList),
133 ARRAY_EXPR => Some(FoldKind::Array), 134 ARRAY_EXPR => Some(FoldKind::Array),
135 RET_TYPE => Some(FoldKind::ReturnType),
134 ASSOC_ITEM_LIST 136 ASSOC_ITEM_LIST
135 | RECORD_FIELD_LIST 137 | RECORD_FIELD_LIST
136 | RECORD_PAT_FIELD_LIST 138 | RECORD_PAT_FIELD_LIST
@@ -300,6 +302,7 @@ mod tests {
300 FoldKind::Statics => "statics", 302 FoldKind::Statics => "statics",
301 FoldKind::Array => "array", 303 FoldKind::Array => "array",
302 FoldKind::WhereClause => "whereclause", 304 FoldKind::WhereClause => "whereclause",
305 FoldKind::ReturnType => "returntype",
303 }; 306 };
304 assert_eq!(kind, &attr.unwrap()); 307 assert_eq!(kind, &attr.unwrap());
305 } 308 }
@@ -560,4 +563,18 @@ where
560"#, 563"#,
561 ) 564 )
562 } 565 }
566
567 #[test]
568 fn fold_return_type() {
569 check(
570 r#"
571fn foo()<fold returntype>-> (
572 bool,
573 bool,
574)</fold> { (true, true) }
575
576fn bar() -> (bool, bool) { (true, true) }
577 "#,
578 )
579 }
563} 580}
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs
index 96447a603..e71a04b6e 100644
--- a/crates/ide_completion/src/completions/keyword.rs
+++ b/crates/ide_completion/src/completions/keyword.rs
@@ -48,91 +48,92 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
48 cov_mark::hit!(no_keyword_completion_in_record_lit); 48 cov_mark::hit!(no_keyword_completion_in_record_lit);
49 return; 49 return;
50 } 50 }
51 let mut add_keyword = |kw, snippet| add_keyword(ctx, acc, kw, snippet);
51 52
52 let expects_assoc_item = ctx.expects_assoc_item(); 53 let expects_assoc_item = ctx.expects_assoc_item();
53 let has_block_expr_parent = ctx.has_block_expr_parent(); 54 let has_block_expr_parent = ctx.has_block_expr_parent();
54 let expects_item = ctx.expects_item(); 55 let expects_item = ctx.expects_item();
56
55 if ctx.has_impl_or_trait_prev_sibling() { 57 if ctx.has_impl_or_trait_prev_sibling() {
56 add_keyword(ctx, acc, "where", "where "); 58 // FIXME this also incorrectly shows up after a complete trait/impl
59 add_keyword("where", "where ");
57 return; 60 return;
58 } 61 }
59 if ctx.previous_token_is(T![unsafe]) { 62 if ctx.previous_token_is(T![unsafe]) {
60 if expects_item || has_block_expr_parent { 63 if expects_item || expects_assoc_item || has_block_expr_parent {
61 add_keyword(ctx, acc, "fn", "fn $1($2) {\n $0\n}") 64 add_keyword("fn", "fn $1($2) {\n $0\n}")
62 } 65 }
63 66
64 if expects_item || has_block_expr_parent { 67 if expects_item || has_block_expr_parent {
65 add_keyword(ctx, acc, "trait", "trait $1 {\n $0\n}"); 68 add_keyword("trait", "trait $1 {\n $0\n}");
66 add_keyword(ctx, acc, "impl", "impl $1 {\n $0\n}"); 69 add_keyword("impl", "impl $1 {\n $0\n}");
67 } 70 }
68 71
69 return; 72 return;
70 } 73 }
74
75 if expects_item || ctx.expects_non_trait_assoc_item() || ctx.expect_record_field() {
76 add_keyword("pub(crate)", "pub(crate) ");
77 add_keyword("pub", "pub ");
78 }
79
80 if expects_item || expects_assoc_item || has_block_expr_parent || ctx.is_match_arm {
81 add_keyword("unsafe", "unsafe ");
82 }
83
71 if expects_item || expects_assoc_item || has_block_expr_parent { 84 if expects_item || expects_assoc_item || has_block_expr_parent {
72 add_keyword(ctx, acc, "fn", "fn $1($2) {\n $0\n}"); 85 add_keyword("fn", "fn $1($2) {\n $0\n}");
86 add_keyword("const", "const $0");
87 add_keyword("type", "type $0");
73 } 88 }
89
74 if expects_item || has_block_expr_parent { 90 if expects_item || has_block_expr_parent {
75 add_keyword(ctx, acc, "use", "use "); 91 add_keyword("use", "use $0");
76 add_keyword(ctx, acc, "impl", "impl $1 {\n $0\n}"); 92 add_keyword("impl", "impl $1 {\n $0\n}");
77 add_keyword(ctx, acc, "trait", "trait $1 {\n $0\n}"); 93 add_keyword("trait", "trait $1 {\n $0\n}");
94 add_keyword("static", "static $0");
95 add_keyword("extern", "extern $0");
96 add_keyword("mod", "mod $0");
78 } 97 }
79 98
80 if expects_item { 99 if expects_item {
81 add_keyword(ctx, acc, "enum", "enum $1 {\n $0\n}"); 100 add_keyword("enum", "enum $1 {\n $0\n}");
82 add_keyword(ctx, acc, "struct", "struct $0"); 101 add_keyword("struct", "struct $0");
83 add_keyword(ctx, acc, "union", "union $1 {\n $0\n}"); 102 add_keyword("union", "union $1 {\n $0\n}");
84 } 103 }
85 104
86 if ctx.is_expr { 105 if ctx.expects_expression() {
87 add_keyword(ctx, acc, "match", "match $1 {\n $0\n}"); 106 add_keyword("match", "match $1 {\n $0\n}");
88 add_keyword(ctx, acc, "while", "while $1 {\n $0\n}"); 107 add_keyword("while", "while $1 {\n $0\n}");
89 add_keyword(ctx, acc, "while let", "while let $1 = $2 {\n $0\n}"); 108 add_keyword("while let", "while let $1 = $2 {\n $0\n}");
90 add_keyword(ctx, acc, "loop", "loop {\n $0\n}"); 109 add_keyword("loop", "loop {\n $0\n}");
91 add_keyword(ctx, acc, "if", "if $1 {\n $0\n}"); 110 add_keyword("if", "if $1 {\n $0\n}");
92 add_keyword(ctx, acc, "if let", "if let $1 = $2 {\n $0\n}"); 111 add_keyword("if let", "if let $1 = $2 {\n $0\n}");
93 add_keyword(ctx, acc, "for", "for $1 in $2 {\n $0\n}"); 112 add_keyword("for", "for $1 in $2 {\n $0\n}");
94 } 113 }
95 114
96 if ctx.previous_token_is(T![if]) || ctx.previous_token_is(T![while]) || has_block_expr_parent { 115 if ctx.previous_token_is(T![if]) || ctx.previous_token_is(T![while]) || has_block_expr_parent {
97 add_keyword(ctx, acc, "let", "let "); 116 add_keyword("let", "let ");
98 } 117 }
99 118
100 if ctx.after_if { 119 if ctx.after_if() {
101 add_keyword(ctx, acc, "else", "else {\n $0\n}"); 120 add_keyword("else", "else {\n $0\n}");
102 add_keyword(ctx, acc, "else if", "else if $1 {\n $0\n}"); 121 add_keyword("else if", "else if $1 {\n $0\n}");
103 }
104 if expects_item || has_block_expr_parent {
105 add_keyword(ctx, acc, "mod", "mod $0");
106 } 122 }
123
107 if ctx.expects_ident_pat_or_ref_expr() { 124 if ctx.expects_ident_pat_or_ref_expr() {
108 add_keyword(ctx, acc, "mut", "mut "); 125 add_keyword("mut", "mut ");
109 }
110 if expects_item || expects_assoc_item || has_block_expr_parent {
111 add_keyword(ctx, acc, "const", "const ");
112 add_keyword(ctx, acc, "type", "type ");
113 }
114 if expects_item || has_block_expr_parent {
115 add_keyword(ctx, acc, "static", "static ");
116 };
117 if expects_item || has_block_expr_parent {
118 add_keyword(ctx, acc, "extern", "extern ");
119 }
120 if expects_item || expects_assoc_item || has_block_expr_parent || ctx.is_match_arm {
121 add_keyword(ctx, acc, "unsafe", "unsafe ");
122 } 126 }
127
123 if ctx.in_loop_body { 128 if ctx.in_loop_body {
124 if ctx.can_be_stmt { 129 if ctx.can_be_stmt {
125 add_keyword(ctx, acc, "continue", "continue;"); 130 add_keyword("continue", "continue;");
126 add_keyword(ctx, acc, "break", "break;"); 131 add_keyword("break", "break;");
127 } else { 132 } else {
128 add_keyword(ctx, acc, "continue", "continue"); 133 add_keyword("continue", "continue");
129 add_keyword(ctx, acc, "break", "break"); 134 add_keyword("break", "break");
130 } 135 }
131 } 136 }
132 if expects_item || ctx.expects_non_trait_assoc_item() || ctx.expect_record_field() {
133 add_keyword(ctx, acc, "pub(crate)", "pub(crate) ");
134 add_keyword(ctx, acc, "pub", "pub ");
135 }
136 137
137 if !ctx.is_trivial_path { 138 if !ctx.is_trivial_path {
138 return; 139 return;
@@ -143,8 +144,6 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
143 }; 144 };
144 145
145 add_keyword( 146 add_keyword(
146 ctx,
147 acc,
148 "return", 147 "return",
149 match (ctx.can_be_stmt, fn_def.ret_type().is_some()) { 148 match (ctx.can_be_stmt, fn_def.ret_type().is_some()) {
150 (true, true) => "return $0;", 149 (true, true) => "return $0;",
@@ -161,15 +160,12 @@ fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet
161 160
162 match ctx.config.snippet_cap { 161 match ctx.config.snippet_cap {
163 Some(cap) => { 162 Some(cap) => {
164 let tmp; 163 if snippet.ends_with('}') && ctx.incomplete_let {
165 let snippet = if snippet.ends_with('}') && ctx.incomplete_let {
166 cov_mark::hit!(let_semi); 164 cov_mark::hit!(let_semi);
167 tmp = format!("{};", snippet); 165 item.insert_snippet(cap, format!("{};", snippet));
168 &tmp
169 } else { 166 } else {
170 snippet 167 item.insert_snippet(cap, snippet);
171 }; 168 }
172 item.insert_snippet(cap, snippet);
173 } 169 }
174 None => { 170 None => {
175 item.insert_text(if snippet.contains('$') { kw } else { snippet }); 171 item.insert_text(if snippet.contains('$') { kw } else { snippet });
@@ -232,21 +228,21 @@ mod tests {
232 check( 228 check(
233 r"m$0", 229 r"m$0",
234 expect![[r#" 230 expect![[r#"
231 kw pub(crate)
232 kw pub
233 kw unsafe
235 kw fn 234 kw fn
235 kw const
236 kw type
236 kw use 237 kw use
237 kw impl 238 kw impl
238 kw trait 239 kw trait
240 kw static
241 kw extern
242 kw mod
239 kw enum 243 kw enum
240 kw struct 244 kw struct
241 kw union 245 kw union
242 kw mod
243 kw const
244 kw type
245 kw static
246 kw extern
247 kw unsafe
248 kw pub(crate)
249 kw pub
250 "#]], 246 "#]],
251 ); 247 );
252 } 248 }
@@ -256,10 +252,16 @@ mod tests {
256 check( 252 check(
257 r"fn quux() { $0 }", 253 r"fn quux() { $0 }",
258 expect![[r#" 254 expect![[r#"
255 kw unsafe
259 kw fn 256 kw fn
257 kw const
258 kw type
260 kw use 259 kw use
261 kw impl 260 kw impl
262 kw trait 261 kw trait
262 kw static
263 kw extern
264 kw mod
263 kw match 265 kw match
264 kw while 266 kw while
265 kw while let 267 kw while let
@@ -268,12 +270,6 @@ mod tests {
268 kw if let 270 kw if let
269 kw for 271 kw for
270 kw let 272 kw let
271 kw mod
272 kw const
273 kw type
274 kw static
275 kw extern
276 kw unsafe
277 kw return 273 kw return
278 "#]], 274 "#]],
279 ); 275 );
@@ -284,10 +280,16 @@ mod tests {
284 check( 280 check(
285 r"fn quux() { if true { $0 } }", 281 r"fn quux() { if true { $0 } }",
286 expect![[r#" 282 expect![[r#"
283 kw unsafe
287 kw fn 284 kw fn
285 kw const
286 kw type
288 kw use 287 kw use
289 kw impl 288 kw impl
290 kw trait 289 kw trait
290 kw static
291 kw extern
292 kw mod
291 kw match 293 kw match
292 kw while 294 kw while
293 kw while let 295 kw while let
@@ -296,12 +298,6 @@ mod tests {
296 kw if let 298 kw if let
297 kw for 299 kw for
298 kw let 300 kw let
299 kw mod
300 kw const
301 kw type
302 kw static
303 kw extern
304 kw unsafe
305 kw return 301 kw return
306 "#]], 302 "#]],
307 ); 303 );
@@ -312,10 +308,16 @@ mod tests {
312 check( 308 check(
313 r#"fn quux() { if true { () } $0 }"#, 309 r#"fn quux() { if true { () } $0 }"#,
314 expect![[r#" 310 expect![[r#"
311 kw unsafe
315 kw fn 312 kw fn
313 kw const
314 kw type
316 kw use 315 kw use
317 kw impl 316 kw impl
318 kw trait 317 kw trait
318 kw static
319 kw extern
320 kw mod
319 kw match 321 kw match
320 kw while 322 kw while
321 kw while let 323 kw while let
@@ -326,12 +328,6 @@ mod tests {
326 kw let 328 kw let
327 kw else 329 kw else
328 kw else if 330 kw else if
329 kw mod
330 kw const
331 kw type
332 kw static
333 kw extern
334 kw unsafe
335 kw return 331 kw return
336 "#]], 332 "#]],
337 ); 333 );
@@ -353,6 +349,7 @@ fn quux() -> i32 {
353} 349}
354"#, 350"#,
355 expect![[r#" 351 expect![[r#"
352 kw unsafe
356 kw match 353 kw match
357 kw while 354 kw while
358 kw while let 355 kw while let
@@ -360,7 +357,6 @@ fn quux() -> i32 {
360 kw if 357 kw if
361 kw if let 358 kw if let
362 kw for 359 kw for
363 kw unsafe
364 kw return 360 kw return
365 "#]], 361 "#]],
366 ); 362 );
@@ -371,10 +367,10 @@ fn quux() -> i32 {
371 check( 367 check(
372 r"trait My { $0 }", 368 r"trait My { $0 }",
373 expect![[r#" 369 expect![[r#"
370 kw unsafe
374 kw fn 371 kw fn
375 kw const 372 kw const
376 kw type 373 kw type
377 kw unsafe
378 "#]], 374 "#]],
379 ); 375 );
380 } 376 }
@@ -384,12 +380,27 @@ fn quux() -> i32 {
384 check( 380 check(
385 r"impl My { $0 }", 381 r"impl My { $0 }",
386 expect![[r#" 382 expect![[r#"
383 kw pub(crate)
384 kw pub
385 kw unsafe
387 kw fn 386 kw fn
388 kw const 387 kw const
389 kw type 388 kw type
390 kw unsafe 389 "#]],
390 );
391 }
392
393 #[test]
394 fn test_keywords_in_impl_def_with_attr() {
395 check(
396 r"impl My { #[foo] $0 }",
397 expect![[r#"
391 kw pub(crate) 398 kw pub(crate)
392 kw pub 399 kw pub
400 kw unsafe
401 kw fn
402 kw const
403 kw type
393 "#]], 404 "#]],
394 ); 405 );
395 } 406 }
@@ -399,10 +410,16 @@ fn quux() -> i32 {
399 check( 410 check(
400 r"fn my() { loop { $0 } }", 411 r"fn my() { loop { $0 } }",
401 expect![[r#" 412 expect![[r#"
413 kw unsafe
402 kw fn 414 kw fn
415 kw const
416 kw type
403 kw use 417 kw use
404 kw impl 418 kw impl
405 kw trait 419 kw trait
420 kw static
421 kw extern
422 kw mod
406 kw match 423 kw match
407 kw while 424 kw while
408 kw while let 425 kw while let
@@ -411,12 +428,6 @@ fn quux() -> i32 {
411 kw if let 428 kw if let
412 kw for 429 kw for
413 kw let 430 kw let
414 kw mod
415 kw const
416 kw type
417 kw static
418 kw extern
419 kw unsafe
420 kw continue 431 kw continue
421 kw break 432 kw break
422 kw return 433 kw return
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs
index c901b358b..ede07f605 100644
--- a/crates/ide_completion/src/completions/unqualified_path.rs
+++ b/crates/ide_completion/src/completions/unqualified_path.rs
@@ -1,7 +1,6 @@
1//! Completion of names from the current scope, e.g. locals and imported items. 1//! Completion of names from the current scope, e.g. locals and imported items.
2 2
3use hir::ScopeDef; 3use hir::ScopeDef;
4use syntax::AstNode;
5 4
6use crate::{CompletionContext, Completions}; 5use crate::{CompletionContext, Completions};
7 6
@@ -24,6 +23,15 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
24 return; 23 return;
25 } 24 }
26 25
26 if ctx.expects_use_tree() {
27 cov_mark::hit!(only_completes_modules_in_import);
28 ctx.scope.process_all_names(&mut |name, res| {
29 if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res {
30 acc.add_resolution(ctx, name.to_string(), &res);
31 }
32 });
33 return;
34 }
27 if let Some(hir::Adt::Enum(e)) = 35 if let Some(hir::Adt::Enum(e)) =
28 ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) 36 ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt())
29 { 37 {
@@ -37,14 +45,6 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
37 cov_mark::hit!(skip_lifetime_completion); 45 cov_mark::hit!(skip_lifetime_completion);
38 return; 46 return;
39 } 47 }
40 if ctx.use_item_syntax.is_some() {
41 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) {
42 if name_ref.syntax().text() == name.to_string().as_str() {
43 cov_mark::hit!(self_fulfilling_completion);
44 return;
45 }
46 }
47 }
48 acc.add_resolution(ctx, name.to_string(), &res); 48 acc.add_resolution(ctx, name.to_string(), &res);
49 }); 49 });
50} 50}
@@ -68,15 +68,17 @@ mod tests {
68 } 68 }
69 69
70 #[test] 70 #[test]
71 fn self_fulfilling_completion() { 71 fn only_completes_modules_in_import() {
72 cov_mark::check!(self_fulfilling_completion); 72 cov_mark::check!(only_completes_modules_in_import);
73 check( 73 check(
74 r#" 74 r#"
75use foo$0 75use f$0
76use std::collections; 76
77struct Foo;
78mod foo {}
77"#, 79"#,
78 expect![[r#" 80 expect![[r#"
79 ?? collections 81 md foo
80 "#]], 82 "#]],
81 ); 83 );
82 } 84 }
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index fbef54408..8d6440cb2 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -17,8 +17,8 @@ use text_edit::Indel;
17 17
18use crate::{ 18use crate::{
19 patterns::{ 19 patterns::{
20 determine_location, for_is_prev2, has_prev_sibling, inside_impl_trait_block, 20 determine_location, determine_prev_sibling, for_is_prev2, inside_impl_trait_block,
21 is_in_loop_body, is_match_arm, previous_token, ImmediateLocation, 21 is_in_loop_body, is_match_arm, previous_token, ImmediateLocation, ImmediatePrevSibling,
22 }, 22 },
23 CompletionConfig, 23 CompletionConfig,
24}; 24};
@@ -29,12 +29,6 @@ pub(crate) enum PatternRefutability {
29 Irrefutable, 29 Irrefutable,
30} 30}
31 31
32#[derive(Copy, Clone, Debug, PartialEq, Eq)]
33pub(crate) enum PrevSibling {
34 Trait,
35 Impl,
36}
37
38/// `CompletionContext` is created early during completion to figure out, where 32/// `CompletionContext` is created early during completion to figure out, where
39/// exactly is the cursor, syntax-wise. 33/// exactly is the cursor, syntax-wise.
40#[derive(Debug)] 34#[derive(Debug)]
@@ -76,6 +70,7 @@ pub(crate) struct CompletionContext<'a> {
76 pub(super) is_param: bool, 70 pub(super) is_param: bool,
77 71
78 pub(super) completion_location: Option<ImmediateLocation>, 72 pub(super) completion_location: Option<ImmediateLocation>,
73 pub(super) prev_sibling: Option<ImmediatePrevSibling>,
79 74
80 /// FIXME: `ActiveParameter` is string-based, which is very very wrong 75 /// FIXME: `ActiveParameter` is string-based, which is very very wrong
81 pub(super) active_parameter: Option<ActiveParameter>, 76 pub(super) active_parameter: Option<ActiveParameter>,
@@ -83,7 +78,6 @@ pub(crate) struct CompletionContext<'a> {
83 pub(super) is_trivial_path: bool, 78 pub(super) is_trivial_path: bool,
84 /// If not a trivial path, the prefix (qualifier). 79 /// If not a trivial path, the prefix (qualifier).
85 pub(super) path_qual: Option<ast::Path>, 80 pub(super) path_qual: Option<ast::Path>,
86 pub(super) after_if: bool,
87 /// `true` if we are a statement or a last expr in the block. 81 /// `true` if we are a statement or a last expr in the block.
88 pub(super) can_be_stmt: bool, 82 pub(super) can_be_stmt: bool,
89 /// `true` if we expect an expression at the cursor position. 83 /// `true` if we expect an expression at the cursor position.
@@ -107,7 +101,6 @@ pub(crate) struct CompletionContext<'a> {
107 101
108 // keyword patterns 102 // keyword patterns
109 pub(super) previous_token: Option<SyntaxToken>, 103 pub(super) previous_token: Option<SyntaxToken>,
110 pub(super) prev_sibling: Option<PrevSibling>,
111 pub(super) in_loop_body: bool, 104 pub(super) in_loop_body: bool,
112 pub(super) is_match_arm: bool, 105 pub(super) is_match_arm: bool,
113 pub(super) incomplete_let: bool, 106 pub(super) incomplete_let: bool,
@@ -173,7 +166,6 @@ impl<'a> CompletionContext<'a> {
173 is_pat_or_const: None, 166 is_pat_or_const: None,
174 is_trivial_path: false, 167 is_trivial_path: false,
175 path_qual: None, 168 path_qual: None,
176 after_if: false,
177 can_be_stmt: false, 169 can_be_stmt: false,
178 is_expr: false, 170 is_expr: false,
179 is_new_item: false, 171 is_new_item: false,
@@ -276,6 +268,10 @@ impl<'a> CompletionContext<'a> {
276 ) 268 )
277 } 269 }
278 270
271 pub(crate) fn expects_use_tree(&self) -> bool {
272 matches!(self.completion_location, Some(ImmediateLocation::Use))
273 }
274
279 pub(crate) fn expects_non_trait_assoc_item(&self) -> bool { 275 pub(crate) fn expects_non_trait_assoc_item(&self) -> bool {
280 matches!(self.completion_location, Some(ImmediateLocation::Impl)) 276 matches!(self.completion_location, Some(ImmediateLocation::Impl))
281 } 277 }
@@ -284,6 +280,10 @@ impl<'a> CompletionContext<'a> {
284 matches!(self.completion_location, Some(ImmediateLocation::ItemList)) 280 matches!(self.completion_location, Some(ImmediateLocation::ItemList))
285 } 281 }
286 282
283 pub(crate) fn expects_expression(&self) -> bool {
284 self.is_expr
285 }
286
287 pub(crate) fn has_block_expr_parent(&self) -> bool { 287 pub(crate) fn has_block_expr_parent(&self) -> bool {
288 matches!(self.completion_location, Some(ImmediateLocation::BlockExpr)) 288 matches!(self.completion_location, Some(ImmediateLocation::BlockExpr))
289 } 289 }
@@ -300,7 +300,14 @@ impl<'a> CompletionContext<'a> {
300 } 300 }
301 301
302 pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool { 302 pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool {
303 self.prev_sibling.is_some() 303 matches!(
304 self.prev_sibling,
305 Some(ImmediatePrevSibling::ImplDefType) | Some(ImmediatePrevSibling::TraitDefName)
306 )
307 }
308
309 pub(crate) fn after_if(&self) -> bool {
310 matches!(self.prev_sibling, Some(ImmediatePrevSibling::IfExpr))
304 } 311 }
305 312
306 pub(crate) fn is_path_disallowed(&self) -> bool { 313 pub(crate) fn is_path_disallowed(&self) -> bool {
@@ -312,15 +319,10 @@ impl<'a> CompletionContext<'a> {
312 319
313 fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) { 320 fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) {
314 let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap(); 321 let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
315 let syntax_element = NodeOrToken::Token(fake_ident_token.clone()); 322 let syntax_element = NodeOrToken::Token(fake_ident_token);
316 self.previous_token = previous_token(syntax_element.clone()); 323 self.previous_token = previous_token(syntax_element.clone());
317 self.in_loop_body = is_in_loop_body(syntax_element.clone()); 324 self.in_loop_body = is_in_loop_body(syntax_element.clone());
318 self.is_match_arm = is_match_arm(syntax_element.clone()); 325 self.is_match_arm = is_match_arm(syntax_element.clone());
319 if has_prev_sibling(syntax_element.clone(), IMPL) {
320 self.prev_sibling = Some(PrevSibling::Impl)
321 } else if has_prev_sibling(syntax_element.clone(), TRAIT) {
322 self.prev_sibling = Some(PrevSibling::Trait)
323 }
324 326
325 self.mod_declaration_under_caret = 327 self.mod_declaration_under_caret =
326 find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset) 328 find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset)
@@ -334,8 +336,6 @@ impl<'a> CompletionContext<'a> {
334 let fn_is_prev = self.previous_token_is(T![fn]); 336 let fn_is_prev = self.previous_token_is(T![fn]);
335 let for_is_prev2 = for_is_prev2(syntax_element.clone()); 337 let for_is_prev2 = for_is_prev2(syntax_element.clone());
336 self.no_completion_required = (fn_is_prev && !inside_impl_trait_block) || for_is_prev2; 338 self.no_completion_required = (fn_is_prev && !inside_impl_trait_block) || for_is_prev2;
337
338 self.completion_location = determine_location(fake_ident_token);
339 } 339 }
340 340
341 fn fill_impl_def(&mut self) { 341 fn fill_impl_def(&mut self) {
@@ -461,6 +461,8 @@ impl<'a> CompletionContext<'a> {
461 Some(it) => it, 461 Some(it) => it,
462 None => return, 462 None => return,
463 }; 463 };
464 self.completion_location = determine_location(&name_like);
465 self.prev_sibling = determine_prev_sibling(&name_like);
464 match name_like { 466 match name_like {
465 ast::NameLike::Lifetime(lifetime) => { 467 ast::NameLike::Lifetime(lifetime) => {
466 self.classify_lifetime(original_file, lifetime, offset); 468 self.classify_lifetime(original_file, lifetime, offset);
@@ -649,17 +651,6 @@ impl<'a> CompletionContext<'a> {
649 }) 651 })
650 .unwrap_or(false); 652 .unwrap_or(false);
651 self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some(); 653 self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some();
652
653 if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) {
654 if let Some(if_expr) =
655 self.sema.find_node_at_offset_with_macros::<ast::IfExpr>(original_file, off)
656 {
657 if if_expr.syntax().text_range().end() < name_ref.syntax().text_range().start()
658 {
659 self.after_if = true;
660 }
661 }
662 }
663 } 654 }
664 655
665 if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { 656 if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs
index 19e42ba43..caf0ef39f 100644
--- a/crates/ide_completion/src/patterns.rs
+++ b/crates/ide_completion/src/patterns.rs
@@ -4,16 +4,24 @@ use syntax::{
4 algo::non_trivia_sibling, 4 algo::non_trivia_sibling,
5 ast::{self, LoopBodyOwner}, 5 ast::{self, LoopBodyOwner},
6 match_ast, AstNode, Direction, NodeOrToken, SyntaxElement, 6 match_ast, AstNode, Direction, NodeOrToken, SyntaxElement,
7 SyntaxKind::{self, *}, 7 SyntaxKind::*,
8 SyntaxNode, SyntaxToken, T, 8 SyntaxNode, SyntaxToken, T,
9}; 9};
10 10
11#[cfg(test)] 11#[cfg(test)]
12use crate::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applicable}; 12use crate::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applicable};
13/// Direct parent container of the cursor position
14#[derive(Copy, Clone, Debug, PartialEq, Eq)]
15pub(crate) enum ImmediatePrevSibling {
16 IfExpr,
17 TraitDefName,
18 ImplDefType,
19}
13 20
14/// Direct parent container of the cursor position 21/// Direct parent container of the cursor position
15#[derive(Copy, Clone, Debug, PartialEq, Eq)] 22#[derive(Copy, Clone, Debug, PartialEq, Eq)]
16pub(crate) enum ImmediateLocation { 23pub(crate) enum ImmediateLocation {
24 Use,
17 Impl, 25 Impl,
18 Trait, 26 Trait,
19 RecordField, 27 RecordField,
@@ -23,30 +31,70 @@ pub(crate) enum ImmediateLocation {
23 ItemList, 31 ItemList,
24} 32}
25 33
26pub(crate) fn determine_location(tok: SyntaxToken) -> Option<ImmediateLocation> { 34pub(crate) fn determine_prev_sibling(name_like: &ast::NameLike) -> Option<ImmediatePrevSibling> {
27 // First "expand" the element we are completing to its maximum so that we can check in what 35 let node = maximize_name_ref(name_like)?;
28 // context it immediately lies. This for example means if the token is a NameRef at the end of 36 let node = match node.parent().and_then(ast::MacroCall::cast) {
29 // a path, we want to look at where the path is in the tree. 37 // When a path is being typed after the name of a trait/type of an impl it is being
30 let node = match tok.parent().and_then(ast::NameLike::cast)? { 38 // parsed as a macro, so when the trait/impl has a block following it an we are between the
31 ast::NameLike::NameRef(name_ref) => { 39 // name and block the macro will attach the block to itself so maximizing fails to take
32 if let Some(segment) = name_ref.syntax().parent().and_then(ast::PathSegment::cast) { 40 // that into account
33 let p = segment.parent_path(); 41 // FIXME path expr and statement have a similar problem with attrs
34 if p.parent_path().is_none() { 42 Some(call)
35 p.syntax() 43 if call.excl_token().is_none()
36 .ancestors() 44 && call.token_tree().map_or(false, |t| t.l_curly_token().is_some())
37 .take_while(|it| it.text_range() == p.syntax().text_range()) 45 && call.semicolon_token().is_none() =>
38 .last()? 46 {
39 } else { 47 call.syntax().clone()
40 return None; 48 }
49 _ => node,
50 };
51 let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?;
52 let res = match_ast! {
53 match prev_sibling {
54 ast::ExprStmt(it) => {
55 let node = it.expr().filter(|_| it.semicolon_token().is_none())?.syntax().clone();
56 match_ast! {
57 match node {
58 ast::IfExpr(_it) => ImmediatePrevSibling::IfExpr,
59 _ => return None,
60 }
41 } 61 }
42 } else { 62 },
43 return None; 63 ast::Trait(it) => if it.assoc_item_list().is_none() {
44 } 64 ImmediatePrevSibling::TraitDefName
65 } else {
66 return None
67 },
68 ast::Impl(it) => if it.assoc_item_list().is_none()
69 && (it.for_token().is_none() || it.self_ty().is_some()) {
70 ImmediatePrevSibling::ImplDefType
71 } else {
72 return None
73 },
74 _ => return None,
45 } 75 }
46 it @ ast::NameLike::Name(_) | it @ ast::NameLike::Lifetime(_) => it.syntax().clone(),
47 }; 76 };
77 Some(res)
78}
79
80pub(crate) fn determine_location(name_like: &ast::NameLike) -> Option<ImmediateLocation> {
81 let node = maximize_name_ref(name_like)?;
48 let parent = match node.parent() { 82 let parent = match node.parent() {
49 Some(parent) => parent, 83 Some(parent) => match ast::MacroCall::cast(parent.clone()) {
84 // When a path is being typed in an (Assoc)ItemList the parser will always emit a macro_call.
85 // This is usually fine as the node expansion code above already accounts for that with
86 // the ancestors call, but there is one exception to this which is that when an attribute
87 // precedes it the code above will not walk the Path to the parent MacroCall as their ranges differ.
88 // FIXME path expr and statement have a similar problem
89 Some(call)
90 if call.excl_token().is_none()
91 && call.token_tree().is_none()
92 && call.semicolon_token().is_none() =>
93 {
94 call.syntax().parent()?
95 }
96 _ => parent,
97 },
50 // SourceFile 98 // SourceFile
51 None => { 99 None => {
52 return match node.kind() { 100 return match node.kind() {
@@ -58,6 +106,7 @@ pub(crate) fn determine_location(tok: SyntaxToken) -> Option<ImmediateLocation>
58 let res = match_ast! { 106 let res = match_ast! {
59 match parent { 107 match parent {
60 ast::IdentPat(_it) => ImmediateLocation::IdentPat, 108 ast::IdentPat(_it) => ImmediateLocation::IdentPat,
109 ast::Use(_it) => ImmediateLocation::Use,
61 ast::BlockExpr(_it) => ImmediateLocation::BlockExpr, 110 ast::BlockExpr(_it) => ImmediateLocation::BlockExpr,
62 ast::SourceFile(_it) => ImmediateLocation::ItemList, 111 ast::SourceFile(_it) => ImmediateLocation::ItemList,
63 ast::ItemList(_it) => ImmediateLocation::ItemList, 112 ast::ItemList(_it) => ImmediateLocation::ItemList,
@@ -74,51 +123,30 @@ pub(crate) fn determine_location(tok: SyntaxToken) -> Option<ImmediateLocation>
74 Some(res) 123 Some(res)
75} 124}
76 125
77#[cfg(test)] 126fn maximize_name_ref(name_like: &ast::NameLike) -> Option<SyntaxNode> {
78fn check_location(code: &str, loc: ImmediateLocation) { 127 // First walk the element we are completing up to its highest node that has the same text range
79 check_pattern_is_applicable(code, |e| { 128 // as the element so that we can check in what context it immediately lies. We only do this for
80 assert_eq!(determine_location(e.into_token().expect("Expected a token")), Some(loc)); 129 // NameRef -> Path as that's the only thing that makes sense to being "expanded" semantically.
81 true 130 // We only wanna do this if the NameRef is the last segment of the path.
82 }); 131 let node = match name_like {
83} 132 ast::NameLike::NameRef(name_ref) => {
84 133 if let Some(segment) = name_ref.syntax().parent().and_then(ast::PathSegment::cast) {
85#[test] 134 let p = segment.parent_path();
86fn test_has_trait_parent() { 135 if p.parent_path().is_none() {
87 check_location(r"trait A { f$0 }", ImmediateLocation::Trait); 136 p.syntax()
88} 137 .ancestors()
89 138 .take_while(|it| it.text_range() == p.syntax().text_range())
90#[test] 139 .last()?
91fn test_has_impl_parent() { 140 } else {
92 check_location(r"impl A { f$0 }", ImmediateLocation::Impl); 141 return None;
93} 142 }
94#[test] 143 } else {
95fn test_has_field_list_parent() { 144 return None;
96 check_location(r"struct Foo { f$0 }", ImmediateLocation::RecordField); 145 }
97 check_location(r"struct Foo { f$0 pub f: i32}", ImmediateLocation::RecordField); 146 }
98} 147 it @ ast::NameLike::Name(_) | it @ ast::NameLike::Lifetime(_) => it.syntax().clone(),
99 148 };
100#[test] 149 Some(node)
101fn test_has_block_expr_parent() {
102 check_location(r"fn my_fn() { let a = 2; f$0 }", ImmediateLocation::BlockExpr);
103}
104
105#[test]
106fn test_has_ident_pat_parent() {
107 check_location(r"fn my_fn(m$0) {}", ImmediateLocation::IdentPat);
108 check_location(r"fn my_fn() { let m$0 }", ImmediateLocation::IdentPat);
109 check_location(r"fn my_fn(&m$0) {}", ImmediateLocation::IdentPat);
110 check_location(r"fn my_fn() { let &m$0 }", ImmediateLocation::IdentPat);
111}
112
113#[test]
114fn test_has_ref_expr_parent() {
115 check_location(r"fn my_fn() { let x = &m$0 foo; }", ImmediateLocation::RefExpr);
116}
117
118#[test]
119fn test_has_item_list_or_source_file_parent() {
120 check_location(r"i$0", ImmediateLocation::ItemList);
121 check_location(r"mod foo { f$0 }", ImmediateLocation::ItemList);
122} 150}
123 151
124pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool { 152pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool {
@@ -170,14 +198,6 @@ fn test_for_is_prev2() {
170 check_pattern_is_applicable(r"for i i$0", for_is_prev2); 198 check_pattern_is_applicable(r"for i i$0", for_is_prev2);
171} 199}
172 200
173pub(crate) fn has_prev_sibling(element: SyntaxElement, kind: SyntaxKind) -> bool {
174 previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == kind).is_some()
175}
176#[test]
177fn test_has_impl_as_prev_sibling() {
178 check_pattern_is_applicable(r"impl A w$0 {}", |it| has_prev_sibling(it, IMPL));
179}
180
181pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool { 201pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool {
182 element 202 element
183 .ancestors() 203 .ancestors()
@@ -226,3 +246,111 @@ fn previous_sibling_or_ancestor_sibling(element: SyntaxElement) -> Option<Syntax
226 non_trivia_sibling(NodeOrToken::Node(prev_sibling_node), Direction::Prev) 246 non_trivia_sibling(NodeOrToken::Node(prev_sibling_node), Direction::Prev)
227 } 247 }
228} 248}
249
250#[cfg(test)]
251mod tests {
252 use super::*;
253
254 fn check_location(code: &str, loc: impl Into<Option<ImmediateLocation>>) {
255 check_pattern_is_applicable(code, |e| {
256 let name = &e.parent().and_then(ast::NameLike::cast).expect("Expected a namelike");
257 assert_eq!(determine_location(name), loc.into());
258 true
259 });
260 }
261
262 fn check_prev_sibling(code: &str, sibling: impl Into<Option<ImmediatePrevSibling>>) {
263 check_pattern_is_applicable(code, |e| {
264 let name = &e.parent().and_then(ast::NameLike::cast).expect("Expected a namelike");
265 assert_eq!(determine_prev_sibling(name), sibling.into());
266 true
267 });
268 }
269
270 #[test]
271 fn test_trait_loc() {
272 check_location(r"trait A { f$0 }", ImmediateLocation::Trait);
273 check_location(r"trait A { #[attr] f$0 }", ImmediateLocation::Trait);
274 check_location(r"trait A { f$0 fn f() {} }", ImmediateLocation::Trait);
275 check_location(r"trait A { fn f() {} f$0 }", ImmediateLocation::Trait);
276 check_location(r"trait A$0 {}", None);
277 check_location(r"trait A { fn f$0 }", None);
278 }
279
280 #[test]
281 fn test_impl_loc() {
282 check_location(r"impl A { f$0 }", ImmediateLocation::Impl);
283 check_location(r"impl A { #[attr] f$0 }", ImmediateLocation::Impl);
284 check_location(r"impl A { f$0 fn f() {} }", ImmediateLocation::Impl);
285 check_location(r"impl A { fn f() {} f$0 }", ImmediateLocation::Impl);
286 check_location(r"impl A$0 {}", None);
287 check_location(r"impl A { fn f$0 }", None);
288 }
289
290 #[test]
291 fn test_use_loc() {
292 check_location(r"use f$0", ImmediateLocation::Use);
293 check_location(r"use f$0;", ImmediateLocation::Use);
294 check_location(r"use f::{f$0}", None);
295 check_location(r"use {f$0}", None);
296 }
297
298 #[test]
299 fn test_record_field_loc() {
300 check_location(r"struct Foo { f$0 }", ImmediateLocation::RecordField);
301 check_location(r"struct Foo { f$0 pub f: i32}", ImmediateLocation::RecordField);
302 check_location(r"struct Foo { pub f: i32, f$0 }", ImmediateLocation::RecordField);
303 }
304
305 #[test]
306 fn test_block_expr_loc() {
307 check_location(r"fn my_fn() { let a = 2; f$0 }", ImmediateLocation::BlockExpr);
308 check_location(r"fn my_fn() { f$0 f }", ImmediateLocation::BlockExpr);
309 }
310
311 #[test]
312 fn test_ident_pat_loc() {
313 check_location(r"fn my_fn(m$0) {}", ImmediateLocation::IdentPat);
314 check_location(r"fn my_fn() { let m$0 }", ImmediateLocation::IdentPat);
315 check_location(r"fn my_fn(&m$0) {}", ImmediateLocation::IdentPat);
316 check_location(r"fn my_fn() { let &m$0 }", ImmediateLocation::IdentPat);
317 }
318
319 #[test]
320 fn test_ref_expr_loc() {
321 check_location(r"fn my_fn() { let x = &m$0 foo; }", ImmediateLocation::RefExpr);
322 }
323
324 #[test]
325 fn test_item_list_loc() {
326 check_location(r"i$0", ImmediateLocation::ItemList);
327 check_location(r"#[attr] i$0", ImmediateLocation::ItemList);
328 check_location(r"fn f() {} i$0", ImmediateLocation::ItemList);
329 check_location(r"mod foo { f$0 }", ImmediateLocation::ItemList);
330 check_location(r"mod foo { #[attr] f$0 }", ImmediateLocation::ItemList);
331 check_location(r"mod foo { fn f() {} f$0 }", ImmediateLocation::ItemList);
332 check_location(r"mod foo$0 {}", None);
333 }
334
335 #[test]
336 fn test_impl_prev_sibling() {
337 check_prev_sibling(r"impl A w$0 ", ImmediatePrevSibling::ImplDefType);
338 check_prev_sibling(r"impl A w$0 {}", ImmediatePrevSibling::ImplDefType);
339 check_prev_sibling(r"impl A for A w$0 ", ImmediatePrevSibling::ImplDefType);
340 check_prev_sibling(r"impl A for A w$0 {}", ImmediatePrevSibling::ImplDefType);
341 check_prev_sibling(r"impl A for w$0 {}", None);
342 check_prev_sibling(r"impl A for w$0", None);
343 }
344
345 #[test]
346 fn test_trait_prev_sibling() {
347 check_prev_sibling(r"trait A w$0 ", ImmediatePrevSibling::TraitDefName);
348 check_prev_sibling(r"trait A w$0 {}", ImmediatePrevSibling::TraitDefName);
349 }
350
351 #[test]
352 fn test_if_expr_prev_sibling() {
353 check_prev_sibling(r"fn foo() { if true {} w$0", ImmediatePrevSibling::IfExpr);
354 check_prev_sibling(r"fn foo() { if true {}; w$0", None);
355 }
356}
diff --git a/crates/ide_completion/src/render/macro_.rs b/crates/ide_completion/src/render/macro_.rs
index 7578ad50b..b90fd3890 100644
--- a/crates/ide_completion/src/render/macro_.rs
+++ b/crates/ide_completion/src/render/macro_.rs
@@ -74,7 +74,11 @@ impl<'a> MacroRender<'a> {
74 if self.needs_bang() && self.ctx.snippet_cap().is_some() { 74 if self.needs_bang() && self.ctx.snippet_cap().is_some() {
75 format!("{}!{}…{}", self.name, self.bra, self.ket) 75 format!("{}!{}…{}", self.name, self.bra, self.ket)
76 } else { 76 } else {
77 self.banged_name() 77 if self.macro_.kind() == hir::MacroKind::Derive {
78 self.name.to_string()
79 } else {
80 self.banged_name()
81 }
78 } 82 }
79 } 83 }
80 84
diff --git a/crates/ide_completion/src/test_utils.rs b/crates/ide_completion/src/test_utils.rs
index 6656fd725..93c7c872c 100644
--- a/crates/ide_completion/src/test_utils.rs
+++ b/crates/ide_completion/src/test_utils.rs
@@ -12,7 +12,7 @@ use ide_db::{
12use itertools::Itertools; 12use itertools::Itertools;
13use stdx::{format_to, trim_indent}; 13use stdx::{format_to, trim_indent};
14use syntax::{AstNode, NodeOrToken, SyntaxElement}; 14use syntax::{AstNode, NodeOrToken, SyntaxElement};
15use test_utils::{assert_eq_text, RangeOrOffset}; 15use test_utils::assert_eq_text;
16 16
17use crate::{item::CompletionKind, CompletionConfig, CompletionItem}; 17use crate::{item::CompletionKind, CompletionConfig, CompletionItem};
18 18
@@ -36,10 +36,7 @@ pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
36 let mut database = RootDatabase::default(); 36 let mut database = RootDatabase::default();
37 database.apply_change(change_fixture.change); 37 database.apply_change(change_fixture.change);
38 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); 38 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
39 let offset = match range_or_offset { 39 let offset = range_or_offset.expect_offset();
40 RangeOrOffset::Range(_) => panic!(),
41 RangeOrOffset::Offset(it) => it,
42 };
43 (database, FilePosition { file_id, offset }) 40 (database, FilePosition { file_id, offset })
44} 41}
45 42
@@ -52,10 +49,11 @@ pub(crate) fn do_completion_with_config(
52 code: &str, 49 code: &str,
53 kind: CompletionKind, 50 kind: CompletionKind,
54) -> Vec<CompletionItem> { 51) -> Vec<CompletionItem> {
55 let mut kind_completions: Vec<CompletionItem> = 52 get_all_items(config, code)
56 get_all_items(config, code).into_iter().filter(|c| c.completion_kind == kind).collect(); 53 .into_iter()
57 kind_completions.sort_by(|l, r| l.label().cmp(r.label())); 54 .filter(|c| c.completion_kind == kind)
58 kind_completions 55 .sorted_by(|l, r| l.label().cmp(r.label()))
56 .collect()
59} 57}
60 58
61pub(crate) fn completion_list(code: &str, kind: CompletionKind) -> String { 59pub(crate) fn completion_list(code: &str, kind: CompletionKind) -> String {
diff --git a/crates/ide_db/src/call_info/tests.rs b/crates/ide_db/src/call_info/tests.rs
index 1aeda08e5..b585085f3 100644
--- a/crates/ide_db/src/call_info/tests.rs
+++ b/crates/ide_db/src/call_info/tests.rs
@@ -1,6 +1,5 @@
1use base_db::{fixture::ChangeFixture, FilePosition}; 1use base_db::{fixture::ChangeFixture, FilePosition};
2use expect_test::{expect, Expect}; 2use expect_test::{expect, Expect};
3use test_utils::RangeOrOffset;
4 3
5use crate::RootDatabase; 4use crate::RootDatabase;
6 5
@@ -10,10 +9,7 @@ pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
10 let mut database = RootDatabase::default(); 9 let mut database = RootDatabase::default();
11 database.apply_change(change_fixture.change); 10 database.apply_change(change_fixture.change);
12 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); 11 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
13 let offset = match range_or_offset { 12 let offset = range_or_offset.expect_offset();
14 RangeOrOffset::Range(_) => panic!(),
15 RangeOrOffset::Offset(it) => it,
16 };
17 (database, FilePosition { file_id, offset }) 13 (database, FilePosition { file_id, offset })
18} 14}
19 15
diff --git a/crates/ide_db/src/traits/tests.rs b/crates/ide_db/src/traits/tests.rs
index 2a5482024..de994407c 100644
--- a/crates/ide_db/src/traits/tests.rs
+++ b/crates/ide_db/src/traits/tests.rs
@@ -2,7 +2,6 @@ use base_db::{fixture::ChangeFixture, FilePosition};
2use expect_test::{expect, Expect}; 2use expect_test::{expect, Expect};
3use hir::Semantics; 3use hir::Semantics;
4use syntax::ast::{self, AstNode}; 4use syntax::ast::{self, AstNode};
5use test_utils::RangeOrOffset;
6 5
7use crate::RootDatabase; 6use crate::RootDatabase;
8 7
@@ -12,10 +11,7 @@ pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
12 let mut database = RootDatabase::default(); 11 let mut database = RootDatabase::default();
13 database.apply_change(change_fixture.change); 12 database.apply_change(change_fixture.change);
14 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); 13 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
15 let offset = match range_or_offset { 14 let offset = range_or_offset.expect_offset();
16 RangeOrOffset::Range(_) => panic!(),
17 RangeOrOffset::Offset(it) => it,
18 };
19 (database, FilePosition { file_id, offset }) 15 (database, FilePosition { file_id, offset })
20} 16}
21 17
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index 6c883dd58..2b842d393 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -158,7 +158,24 @@ fn run_server() -> Result<()> {
158 let initialize_params = 158 let initialize_params =
159 from_json::<lsp_types::InitializeParams>("InitializeParams", initialize_params)?; 159 from_json::<lsp_types::InitializeParams>("InitializeParams", initialize_params)?;
160 160
161 let server_capabilities = rust_analyzer::server_capabilities(&initialize_params.capabilities); 161 let root_path = match initialize_params
162 .root_uri
163 .and_then(|it| it.to_file_path().ok())
164 .and_then(|it| AbsPathBuf::try_from(it).ok())
165 {
166 Some(it) => it,
167 None => {
168 let cwd = env::current_dir()?;
169 AbsPathBuf::assert(cwd)
170 }
171 };
172
173 let mut config = Config::new(root_path, initialize_params.capabilities);
174 if let Some(json) = initialize_params.initialization_options {
175 config.update(json);
176 }
177
178 let server_capabilities = rust_analyzer::server_capabilities(&config);
162 179
163 let initialize_result = lsp_types::InitializeResult { 180 let initialize_result = lsp_types::InitializeResult {
164 capabilities: server_capabilities, 181 capabilities: server_capabilities,
@@ -166,11 +183,7 @@ fn run_server() -> Result<()> {
166 name: String::from("rust-analyzer"), 183 name: String::from("rust-analyzer"),
167 version: Some(String::from(env!("REV"))), 184 version: Some(String::from(env!("REV"))),
168 }), 185 }),
169 offset_encoding: if supports_utf8(&initialize_params.capabilities) { 186 offset_encoding: if supports_utf8(&config.caps) { Some("utf-8".to_string()) } else { None },
170 Some("utf-8".to_string())
171 } else {
172 None
173 },
174 }; 187 };
175 188
176 let initialize_result = serde_json::to_value(initialize_result).unwrap(); 189 let initialize_result = serde_json::to_value(initialize_result).unwrap();
@@ -181,47 +194,26 @@ fn run_server() -> Result<()> {
181 log::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default()); 194 log::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default());
182 } 195 }
183 196
184 let config = { 197 if config.linked_projects().is_empty() && config.detached_files().is_empty() {
185 let root_path = match initialize_params 198 let workspace_roots = initialize_params
186 .root_uri 199 .workspace_folders
187 .and_then(|it| it.to_file_path().ok()) 200 .map(|workspaces| {
188 .and_then(|it| AbsPathBuf::try_from(it).ok()) 201 workspaces
189 { 202 .into_iter()
190 Some(it) => it, 203 .filter_map(|it| it.uri.to_file_path().ok())
191 None => { 204 .filter_map(|it| AbsPathBuf::try_from(it).ok())
192 let cwd = env::current_dir()?; 205 .collect::<Vec<_>>()
193 AbsPathBuf::assert(cwd) 206 })
194 } 207 .filter(|workspaces| !workspaces.is_empty())
195 }; 208 .unwrap_or_else(|| vec![config.root_path.clone()]);
196 209
197 let mut config = Config::new(root_path, initialize_params.capabilities); 210 let discovered = ProjectManifest::discover_all(&workspace_roots);
198 if let Some(json) = initialize_params.initialization_options { 211 log::info!("discovered projects: {:?}", discovered);
199 config.update(json); 212 if discovered.is_empty() {
200 } 213 log::error!("failed to find any projects in {:?}", workspace_roots);
201
202 if config.linked_projects().is_empty() && config.detached_files().is_empty() {
203 let workspace_roots = initialize_params
204 .workspace_folders
205 .map(|workspaces| {
206 workspaces
207 .into_iter()
208 .filter_map(|it| it.uri.to_file_path().ok())
209 .filter_map(|it| AbsPathBuf::try_from(it).ok())
210 .collect::<Vec<_>>()
211 })
212 .filter(|workspaces| !workspaces.is_empty())
213 .unwrap_or_else(|| vec![config.root_path.clone()]);
214
215 let discovered = ProjectManifest::discover_all(&workspace_roots);
216 log::info!("discovered projects: {:?}", discovered);
217 if discovered.is_empty() {
218 log::error!("failed to find any projects in {:?}", workspace_roots);
219 }
220 config.discovered_projects = Some(discovered);
221 } 214 }
222 215 config.discovered_projects = Some(discovered);
223 config 216 }
224 };
225 217
226 rust_analyzer::main_loop(config, connection)?; 218 rust_analyzer::main_loop(config, connection)?;
227 219
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index 4d88932ca..fe5255240 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -1,6 +1,4 @@
1//! Advertises the capabilities of the LSP Server. 1//! Advertises the capabilities of the LSP Server.
2use std::env;
3
4use lsp_types::{ 2use lsp_types::{
5 CallHierarchyServerCapability, ClientCapabilities, CodeActionKind, CodeActionOptions, 3 CallHierarchyServerCapability, ClientCapabilities, CodeActionKind, CodeActionOptions,
6 CodeActionProviderCapability, CodeLensOptions, CompletionOptions, 4 CodeActionProviderCapability, CodeLensOptions, CompletionOptions,
@@ -15,24 +13,21 @@ use lsp_types::{
15}; 13};
16use serde_json::json; 14use serde_json::json;
17 15
16use crate::config::{Config, RustfmtConfig};
18use crate::semantic_tokens; 17use crate::semantic_tokens;
19 18
20pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabilities { 19pub fn server_capabilities(config: &Config) -> ServerCapabilities {
21 ServerCapabilities { 20 ServerCapabilities {
22 text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions { 21 text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions {
23 open_close: Some(true), 22 open_close: Some(true),
24 change: Some(if env::var("RA_NO_INCREMENTAL_SYNC").is_ok() { 23 change: Some(TextDocumentSyncKind::Incremental),
25 TextDocumentSyncKind::Full
26 } else {
27 TextDocumentSyncKind::Incremental
28 }),
29 will_save: None, 24 will_save: None,
30 will_save_wait_until: None, 25 will_save_wait_until: None,
31 save: Some(SaveOptions::default().into()), 26 save: Some(SaveOptions::default().into()),
32 })), 27 })),
33 hover_provider: Some(HoverProviderCapability::Simple(true)), 28 hover_provider: Some(HoverProviderCapability::Simple(true)),
34 completion_provider: Some(CompletionOptions { 29 completion_provider: Some(CompletionOptions {
35 resolve_provider: completions_resolve_provider(client_caps), 30 resolve_provider: completions_resolve_provider(&config.caps),
36 trigger_characters: Some(vec![":".to_string(), ".".to_string(), "'".to_string()]), 31 trigger_characters: Some(vec![":".to_string(), ".".to_string(), "'".to_string()]),
37 all_commit_characters: None, 32 all_commit_characters: None,
38 completion_item: None, 33 completion_item: None,
@@ -51,10 +46,13 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
51 document_highlight_provider: Some(OneOf::Left(true)), 46 document_highlight_provider: Some(OneOf::Left(true)),
52 document_symbol_provider: Some(OneOf::Left(true)), 47 document_symbol_provider: Some(OneOf::Left(true)),
53 workspace_symbol_provider: Some(OneOf::Left(true)), 48 workspace_symbol_provider: Some(OneOf::Left(true)),
54 code_action_provider: Some(code_action_capabilities(client_caps)), 49 code_action_provider: Some(code_action_capabilities(&config.caps)),
55 code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }), 50 code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }),
56 document_formatting_provider: Some(OneOf::Left(true)), 51 document_formatting_provider: Some(OneOf::Left(true)),
57 document_range_formatting_provider: Some(OneOf::Left(true)), 52 document_range_formatting_provider: match config.rustfmt() {
53 RustfmtConfig::Rustfmt { enable_range_formatting: true, .. } => Some(OneOf::Left(true)),
54 _ => Some(OneOf::Left(false)),
55 },
58 document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions { 56 document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions {
59 first_trigger_character: "=".to_string(), 57 first_trigger_character: "=".to_string(),
60 more_trigger_character: Some(vec![".".to_string(), ">".to_string(), "{".to_string()]), 58 more_trigger_character: Some(vec![".".to_string(), ">".to_string(), "{".to_string()]),
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 7620a2fe1..b6a1124a5 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -238,7 +238,7 @@ impl Default for ConfigData {
238 238
239#[derive(Debug, Clone)] 239#[derive(Debug, Clone)]
240pub struct Config { 240pub struct Config {
241 caps: lsp_types::ClientCapabilities, 241 pub caps: lsp_types::ClientCapabilities,
242 data: ConfigData, 242 data: ConfigData,
243 detached_files: Vec<AbsPathBuf>, 243 detached_files: Vec<AbsPathBuf>,
244 pub discovered_projects: Option<Vec<ProjectManifest>>, 244 pub discovered_projects: Option<Vec<ProjectManifest>>,
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 6d18d0ffc..f5c8535a2 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -534,6 +534,7 @@ pub(crate) fn folding_range(
534 | FoldKind::Consts 534 | FoldKind::Consts
535 | FoldKind::Statics 535 | FoldKind::Statics
536 | FoldKind::WhereClause 536 | FoldKind::WhereClause
537 | FoldKind::ReturnType
537 | FoldKind::Array => None, 538 | FoldKind::Array => None,
538 }; 539 };
539 540
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index fce4fd6bf..bd017567c 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -96,6 +96,21 @@ pub enum RangeOrOffset {
96 Offset(TextSize), 96 Offset(TextSize),
97} 97}
98 98
99impl RangeOrOffset {
100 pub fn expect_offset(self) -> TextSize {
101 match self {
102 RangeOrOffset::Offset(it) => it,
103 RangeOrOffset::Range(_) => panic!("expected an offset but got a range instead"),
104 }
105 }
106 pub fn expect_range(self) -> TextRange {
107 match self {
108 RangeOrOffset::Range(it) => it,
109 RangeOrOffset::Offset(_) => panic!("expected a range but got an offset"),
110 }
111 }
112}
113
99impl From<RangeOrOffset> for TextRange { 114impl From<RangeOrOffset> for TextRange {
100 fn from(selection: RangeOrOffset) -> Self { 115 fn from(selection: RangeOrOffset) -> Self {
101 match selection { 116 match selection {