diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/base_db/src/fixture.rs | 10 | ||||
-rw-r--r-- | crates/hir_def/src/nameres/tests/incremental.rs | 74 | ||||
-rw-r--r-- | crates/hir_ty/src/tests.rs | 45 | ||||
-rw-r--r-- | crates/hir_ty/src/tests/incremental.rs | 51 | ||||
-rw-r--r-- | crates/ide/src/annotations.rs | 34 | ||||
-rw-r--r-- | crates/ide/src/fixture.rs | 17 | ||||
-rwxr-xr-x | crates/ide/src/folding_ranges.rs | 17 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/keyword.rs | 197 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/unqualified_path.rs | 30 | ||||
-rw-r--r-- | crates/ide_completion/src/context.rs | 53 | ||||
-rw-r--r-- | crates/ide_completion/src/patterns.rs | 276 | ||||
-rw-r--r-- | crates/ide_completion/src/render/macro_.rs | 6 | ||||
-rw-r--r-- | crates/ide_completion/src/test_utils.rs | 16 | ||||
-rw-r--r-- | crates/ide_db/src/call_info/tests.rs | 6 | ||||
-rw-r--r-- | crates/ide_db/src/traits/tests.rs | 6 | ||||
-rw-r--r-- | crates/rust-analyzer/src/bin/main.rs | 84 | ||||
-rw-r--r-- | crates/rust-analyzer/src/caps.rs | 20 | ||||
-rw-r--r-- | crates/rust-analyzer/src/config.rs | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/src/to_proto.rs | 1 | ||||
-rw-r--r-- | crates/test_utils/src/lib.rs | 15 |
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 @@ | |||
1 | use std::sync::Arc; | 1 | use std::sync::Arc; |
2 | 2 | ||
3 | use base_db::SourceDatabaseExt; | 3 | use base_db::{salsa::SweepStrategy, SourceDatabaseExt}; |
4 | |||
5 | use crate::{AdtId, ModuleDefId}; | ||
4 | 6 | ||
5 | use super::*; | 7 | use 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] | ||
170 | fn 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#" | ||
177 | pub struct S; | ||
178 | pub union U {} | ||
179 | pub enum E { | ||
180 | Variant, | ||
181 | } | ||
182 | pub fn f(_: S) { $0 } | ||
183 | pub trait Tr {} | ||
184 | impl Tr for () {} | ||
185 | pub const C: u8 = 0; | ||
186 | pub static ST: u8 = 0; | ||
187 | pub 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; | |||
7 | mod method_resolution; | 7 | mod method_resolution; |
8 | mod macros; | 8 | mod macros; |
9 | mod display_source_code; | 9 | mod display_source_code; |
10 | mod incremental; | ||
10 | 11 | ||
11 | use std::{env, sync::Arc}; | 12 | use 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] | ||
321 | fn 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 | |||
364 | fn check_infer(ra_fixture: &str, expect: Expect) { | 321 | fn 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 @@ | |||
1 | use std::sync::Arc; | ||
2 | |||
3 | use base_db::{fixture::WithFixture, SourceDatabaseExt}; | ||
4 | |||
5 | use crate::{db::HirDatabase, test_db::TestDB}; | ||
6 | |||
7 | use super::visit_module; | ||
8 | |||
9 | #[test] | ||
10 | fn 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. |
2 | use ide_db::base_db::fixture::ChangeFixture; | 2 | use ide_db::base_db::fixture::ChangeFixture; |
3 | use syntax::{TextRange, TextSize}; | 3 | use syntax::{TextRange, TextSize}; |
4 | use test_utils::{extract_annotations, RangeOrOffset}; | 4 | use test_utils::extract_annotations; |
5 | 5 | ||
6 | use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange}; | 6 | use 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#" | ||
571 | fn foo()<fold returntype>-> ( | ||
572 | bool, | ||
573 | bool, | ||
574 | )</fold> { (true, true) } | ||
575 | |||
576 | fn 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 | ||
3 | use hir::ScopeDef; | 3 | use hir::ScopeDef; |
4 | use syntax::AstNode; | ||
5 | 4 | ||
6 | use crate::{CompletionContext, Completions}; | 5 | use 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#" |
75 | use foo$0 | 75 | use f$0 |
76 | use std::collections; | 76 | |
77 | struct Foo; | ||
78 | mod 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 | ||
18 | use crate::{ | 18 | use 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)] | ||
33 | pub(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)] |
12 | use crate::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applicable}; | 12 | use 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)] | ||
15 | pub(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)] |
16 | pub(crate) enum ImmediateLocation { | 23 | pub(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 | ||
26 | pub(crate) fn determine_location(tok: SyntaxToken) -> Option<ImmediateLocation> { | 34 | pub(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 | |||
80 | pub(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)] | 126 | fn maximize_name_ref(name_like: &ast::NameLike) -> Option<SyntaxNode> { |
78 | fn 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(); |
86 | fn 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()? |
91 | fn 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 { |
95 | fn 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) |
101 | fn test_has_block_expr_parent() { | ||
102 | check_location(r"fn my_fn() { let a = 2; f$0 }", ImmediateLocation::BlockExpr); | ||
103 | } | ||
104 | |||
105 | #[test] | ||
106 | fn 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] | ||
114 | fn test_has_ref_expr_parent() { | ||
115 | check_location(r"fn my_fn() { let x = &m$0 foo; }", ImmediateLocation::RefExpr); | ||
116 | } | ||
117 | |||
118 | #[test] | ||
119 | fn 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 | ||
124 | pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool { | 152 | pub(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 | ||
173 | pub(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] | ||
177 | fn test_has_impl_as_prev_sibling() { | ||
178 | check_pattern_is_applicable(r"impl A w$0 {}", |it| has_prev_sibling(it, IMPL)); | ||
179 | } | ||
180 | |||
181 | pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool { | 201 | pub(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)] | ||
251 | mod 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::{ | |||
12 | use itertools::Itertools; | 12 | use itertools::Itertools; |
13 | use stdx::{format_to, trim_indent}; | 13 | use stdx::{format_to, trim_indent}; |
14 | use syntax::{AstNode, NodeOrToken, SyntaxElement}; | 14 | use syntax::{AstNode, NodeOrToken, SyntaxElement}; |
15 | use test_utils::{assert_eq_text, RangeOrOffset}; | 15 | use test_utils::assert_eq_text; |
16 | 16 | ||
17 | use crate::{item::CompletionKind, CompletionConfig, CompletionItem}; | 17 | use 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 | ||
61 | pub(crate) fn completion_list(code: &str, kind: CompletionKind) -> String { | 59 | pub(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 @@ | |||
1 | use base_db::{fixture::ChangeFixture, FilePosition}; | 1 | use base_db::{fixture::ChangeFixture, FilePosition}; |
2 | use expect_test::{expect, Expect}; | 2 | use expect_test::{expect, Expect}; |
3 | use test_utils::RangeOrOffset; | ||
4 | 3 | ||
5 | use crate::RootDatabase; | 4 | use 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}; | |||
2 | use expect_test::{expect, Expect}; | 2 | use expect_test::{expect, Expect}; |
3 | use hir::Semantics; | 3 | use hir::Semantics; |
4 | use syntax::ast::{self, AstNode}; | 4 | use syntax::ast::{self, AstNode}; |
5 | use test_utils::RangeOrOffset; | ||
6 | 5 | ||
7 | use crate::RootDatabase; | 6 | use 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. |
2 | use std::env; | ||
3 | |||
4 | use lsp_types::{ | 2 | use 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 | }; |
16 | use serde_json::json; | 14 | use serde_json::json; |
17 | 15 | ||
16 | use crate::config::{Config, RustfmtConfig}; | ||
18 | use crate::semantic_tokens; | 17 | use crate::semantic_tokens; |
19 | 18 | ||
20 | pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabilities { | 19 | pub 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)] |
240 | pub struct Config { | 240 | pub 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 | ||
99 | impl 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 | |||
99 | impl From<RangeOrOffset> for TextRange { | 114 | impl From<RangeOrOffset> for TextRange { |
100 | fn from(selection: RangeOrOffset) -> Self { | 115 | fn from(selection: RangeOrOffset) -> Self { |
101 | match selection { | 116 | match selection { |