diff options
Diffstat (limited to 'crates/ide_completion')
-rw-r--r-- | crates/ide_completion/src/completions.rs | 113 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/keyword.rs | 36 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/pattern.rs | 2 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/qualified_path.rs | 18 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/snippet.rs | 17 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/unqualified_path.rs | 2 | ||||
-rw-r--r-- | crates/ide_completion/src/lib.rs | 125 | ||||
-rw-r--r-- | crates/ide_completion/src/tests.rs | 64 | ||||
-rw-r--r-- | crates/ide_completion/src/tests/item_list.rs | 172 |
9 files changed, 302 insertions, 247 deletions
diff --git a/crates/ide_completion/src/completions.rs b/crates/ide_completion/src/completions.rs index 783305005..cba5eb0c6 100644 --- a/crates/ide_completion/src/completions.rs +++ b/crates/ide_completion/src/completions.rs | |||
@@ -41,9 +41,9 @@ pub struct Completions { | |||
41 | buf: Vec<CompletionItem>, | 41 | buf: Vec<CompletionItem>, |
42 | } | 42 | } |
43 | 43 | ||
44 | impl Into<Vec<CompletionItem>> for Completions { | 44 | impl From<Completions> for Vec<CompletionItem> { |
45 | fn into(self) -> Vec<CompletionItem> { | 45 | fn from(val: Completions) -> Self { |
46 | self.buf | 46 | val.buf |
47 | } | 47 | } |
48 | } | 48 | } |
49 | 49 | ||
@@ -74,35 +74,6 @@ impl Completions { | |||
74 | items.into_iter().for_each(|item| self.add(item.into())) | 74 | items.into_iter().for_each(|item| self.add(item.into())) |
75 | } | 75 | } |
76 | 76 | ||
77 | pub(crate) fn add_field( | ||
78 | &mut self, | ||
79 | ctx: &CompletionContext, | ||
80 | receiver: Option<hir::Name>, | ||
81 | field: hir::Field, | ||
82 | ty: &hir::Type, | ||
83 | ) { | ||
84 | let item = render_field(RenderContext::new(ctx), receiver, field, ty); | ||
85 | self.add(item); | ||
86 | } | ||
87 | |||
88 | pub(crate) fn add_tuple_field( | ||
89 | &mut self, | ||
90 | ctx: &CompletionContext, | ||
91 | receiver: Option<hir::Name>, | ||
92 | field: usize, | ||
93 | ty: &hir::Type, | ||
94 | ) { | ||
95 | let item = render_tuple_field(RenderContext::new(ctx), receiver, field, ty); | ||
96 | self.add(item); | ||
97 | } | ||
98 | |||
99 | pub(crate) fn add_static_lifetime(&mut self, ctx: &CompletionContext) { | ||
100 | let mut item = | ||
101 | CompletionItem::new(CompletionKind::Reference, ctx.source_range(), "'static"); | ||
102 | item.kind(CompletionItemKind::SymbolKind(SymbolKind::LifetimeParam)); | ||
103 | self.add(item.build()); | ||
104 | } | ||
105 | |||
106 | pub(crate) fn add_resolution( | 77 | pub(crate) fn add_resolution( |
107 | &mut self, | 78 | &mut self, |
108 | ctx: &CompletionContext, | 79 | ctx: &CompletionContext, |
@@ -144,72 +115,102 @@ impl Completions { | |||
144 | self.add_opt(render_method(RenderContext::new(ctx), None, receiver, local_name, func)); | 115 | self.add_opt(render_method(RenderContext::new(ctx), None, receiver, local_name, func)); |
145 | } | 116 | } |
146 | 117 | ||
147 | pub(crate) fn add_variant_pat( | 118 | pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) { |
119 | self.add_opt(render_const(RenderContext::new(ctx), constant)); | ||
120 | } | ||
121 | |||
122 | pub(crate) fn add_type_alias(&mut self, ctx: &CompletionContext, type_alias: hir::TypeAlias) { | ||
123 | self.add_opt(render_type_alias(RenderContext::new(ctx), type_alias)); | ||
124 | } | ||
125 | |||
126 | pub(crate) fn add_type_alias_with_eq( | ||
148 | &mut self, | 127 | &mut self, |
149 | ctx: &CompletionContext, | 128 | ctx: &CompletionContext, |
150 | variant: hir::Variant, | 129 | type_alias: hir::TypeAlias, |
151 | local_name: Option<hir::Name>, | ||
152 | ) { | 130 | ) { |
153 | self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, local_name, None)); | 131 | self.add_opt(render_type_alias_with_eq(RenderContext::new(ctx), type_alias)); |
154 | } | 132 | } |
155 | 133 | ||
156 | pub(crate) fn add_qualified_variant_pat( | 134 | pub(crate) fn add_qualified_enum_variant( |
157 | &mut self, | 135 | &mut self, |
158 | ctx: &CompletionContext, | 136 | ctx: &CompletionContext, |
159 | variant: hir::Variant, | 137 | variant: hir::Variant, |
160 | path: hir::ModPath, | 138 | path: hir::ModPath, |
161 | ) { | 139 | ) { |
162 | self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, None, Some(path))); | 140 | let item = render_variant(RenderContext::new(ctx), None, None, variant, Some(path)); |
141 | self.add(item); | ||
163 | } | 142 | } |
164 | 143 | ||
165 | pub(crate) fn add_struct_pat( | 144 | pub(crate) fn add_enum_variant( |
166 | &mut self, | 145 | &mut self, |
167 | ctx: &CompletionContext, | 146 | ctx: &CompletionContext, |
168 | strukt: hir::Struct, | 147 | variant: hir::Variant, |
169 | local_name: Option<hir::Name>, | 148 | local_name: Option<hir::Name>, |
170 | ) { | 149 | ) { |
171 | self.add_opt(render_struct_pat(RenderContext::new(ctx), strukt, local_name)); | 150 | let item = render_variant(RenderContext::new(ctx), None, local_name, variant, None); |
151 | self.add(item); | ||
172 | } | 152 | } |
173 | 153 | ||
174 | pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) { | 154 | pub(crate) fn add_field( |
175 | self.add_opt(render_const(RenderContext::new(ctx), constant)); | 155 | &mut self, |
156 | ctx: &CompletionContext, | ||
157 | receiver: Option<hir::Name>, | ||
158 | field: hir::Field, | ||
159 | ty: &hir::Type, | ||
160 | ) { | ||
161 | let item = render_field(RenderContext::new(ctx), receiver, field, ty); | ||
162 | self.add(item); | ||
176 | } | 163 | } |
177 | 164 | ||
178 | pub(crate) fn add_type_alias(&mut self, ctx: &CompletionContext, type_alias: hir::TypeAlias) { | 165 | pub(crate) fn add_tuple_field( |
179 | self.add_opt(render_type_alias(RenderContext::new(ctx), type_alias)); | 166 | &mut self, |
167 | ctx: &CompletionContext, | ||
168 | receiver: Option<hir::Name>, | ||
169 | field: usize, | ||
170 | ty: &hir::Type, | ||
171 | ) { | ||
172 | let item = render_tuple_field(RenderContext::new(ctx), receiver, field, ty); | ||
173 | self.add(item); | ||
180 | } | 174 | } |
181 | 175 | ||
182 | pub(crate) fn add_type_alias_with_eq( | 176 | pub(crate) fn add_static_lifetime(&mut self, ctx: &CompletionContext) { |
177 | let mut item = | ||
178 | CompletionItem::new(CompletionKind::Reference, ctx.source_range(), "'static"); | ||
179 | item.kind(CompletionItemKind::SymbolKind(SymbolKind::LifetimeParam)); | ||
180 | self.add(item.build()); | ||
181 | } | ||
182 | |||
183 | pub(crate) fn add_variant_pat( | ||
183 | &mut self, | 184 | &mut self, |
184 | ctx: &CompletionContext, | 185 | ctx: &CompletionContext, |
185 | type_alias: hir::TypeAlias, | 186 | variant: hir::Variant, |
187 | local_name: Option<hir::Name>, | ||
186 | ) { | 188 | ) { |
187 | self.add_opt(render_type_alias_with_eq(RenderContext::new(ctx), type_alias)); | 189 | self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, local_name, None)); |
188 | } | 190 | } |
189 | 191 | ||
190 | pub(crate) fn add_qualified_enum_variant( | 192 | pub(crate) fn add_qualified_variant_pat( |
191 | &mut self, | 193 | &mut self, |
192 | ctx: &CompletionContext, | 194 | ctx: &CompletionContext, |
193 | variant: hir::Variant, | 195 | variant: hir::Variant, |
194 | path: hir::ModPath, | 196 | path: hir::ModPath, |
195 | ) { | 197 | ) { |
196 | let item = render_variant(RenderContext::new(ctx), None, None, variant, Some(path)); | 198 | self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, None, Some(path))); |
197 | self.add(item); | ||
198 | } | 199 | } |
199 | 200 | ||
200 | pub(crate) fn add_enum_variant( | 201 | pub(crate) fn add_struct_pat( |
201 | &mut self, | 202 | &mut self, |
202 | ctx: &CompletionContext, | 203 | ctx: &CompletionContext, |
203 | variant: hir::Variant, | 204 | strukt: hir::Struct, |
204 | local_name: Option<hir::Name>, | 205 | local_name: Option<hir::Name>, |
205 | ) { | 206 | ) { |
206 | let item = render_variant(RenderContext::new(ctx), None, local_name, variant, None); | 207 | self.add_opt(render_struct_pat(RenderContext::new(ctx), strukt, local_name)); |
207 | self.add(item); | ||
208 | } | 208 | } |
209 | } | 209 | } |
210 | 210 | ||
211 | /// Calls the callback for each variant of the provided enum with the path to the variant. | 211 | /// Calls the callback for each variant of the provided enum with the path to the variant. |
212 | fn complete_enum_variants( | 212 | /// Skips variants that are visible with single segment paths. |
213 | fn enum_variants_with_paths( | ||
213 | acc: &mut Completions, | 214 | acc: &mut Completions, |
214 | ctx: &CompletionContext, | 215 | ctx: &CompletionContext, |
215 | enum_: hir::Enum, | 216 | enum_: hir::Enum, |
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs index 0fccbeccf..2c42438d6 100644 --- a/crates/ide_completion/src/completions/keyword.rs +++ b/crates/ide_completion/src/completions/keyword.rs | |||
@@ -231,30 +231,6 @@ mod tests { | |||
231 | } | 231 | } |
232 | 232 | ||
233 | #[test] | 233 | #[test] |
234 | fn test_keywords_at_source_file_level() { | ||
235 | check( | ||
236 | r"m$0", | ||
237 | expect![[r#" | ||
238 | kw pub(crate) | ||
239 | kw pub | ||
240 | kw unsafe | ||
241 | kw fn | ||
242 | kw const | ||
243 | kw type | ||
244 | kw use | ||
245 | kw impl | ||
246 | kw trait | ||
247 | kw static | ||
248 | kw extern | ||
249 | kw mod | ||
250 | kw enum | ||
251 | kw struct | ||
252 | kw union | ||
253 | "#]], | ||
254 | ); | ||
255 | } | ||
256 | |||
257 | #[test] | ||
258 | fn test_keywords_in_function() { | 234 | fn test_keywords_in_function() { |
259 | check( | 235 | check( |
260 | r"fn quux() { $0 }", | 236 | r"fn quux() { $0 }", |
@@ -443,18 +419,6 @@ fn quux() -> i32 { | |||
443 | } | 419 | } |
444 | 420 | ||
445 | #[test] | 421 | #[test] |
446 | fn test_keywords_after_unsafe_in_item_list() { | ||
447 | check( | ||
448 | r"unsafe $0", | ||
449 | expect![[r#" | ||
450 | kw fn | ||
451 | kw trait | ||
452 | kw impl | ||
453 | "#]], | ||
454 | ); | ||
455 | } | ||
456 | |||
457 | #[test] | ||
458 | fn test_keywords_after_unsafe_in_block_expr() { | 422 | fn test_keywords_after_unsafe_in_block_expr() { |
459 | check( | 423 | check( |
460 | r"fn my_fn() { unsafe $0 }", | 424 | r"fn my_fn() { unsafe $0 }", |
diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs index 1daa8595a..e45b2a1ea 100644 --- a/crates/ide_completion/src/completions/pattern.rs +++ b/crates/ide_completion/src/completions/pattern.rs | |||
@@ -13,7 +13,7 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { | |||
13 | if let Some(hir::Adt::Enum(e)) = | 13 | if let Some(hir::Adt::Enum(e)) = |
14 | ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) | 14 | ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) |
15 | { | 15 | { |
16 | super::complete_enum_variants(acc, ctx, e, |acc, ctx, variant, path| { | 16 | super::enum_variants_with_paths(acc, ctx, e, |acc, ctx, variant, path| { |
17 | acc.add_qualified_variant_pat(ctx, variant, path.clone()); | 17 | acc.add_qualified_variant_pat(ctx, variant, path.clone()); |
18 | acc.add_qualified_enum_variant(ctx, variant, path); | 18 | acc.add_qualified_enum_variant(ctx, variant, path); |
19 | }); | 19 | }); |
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs index 1643eeed4..165b9e6a5 100644 --- a/crates/ide_completion/src/completions/qualified_path.rs +++ b/crates/ide_completion/src/completions/qualified_path.rs | |||
@@ -714,24 +714,6 @@ impl MyStruct { | |||
714 | } | 714 | } |
715 | 715 | ||
716 | #[test] | 716 | #[test] |
717 | fn completes_in_item_list() { | ||
718 | check( | ||
719 | r#" | ||
720 | struct MyStruct {} | ||
721 | #[macro_export] | ||
722 | macro_rules! foo {} | ||
723 | mod bar {} | ||
724 | |||
725 | crate::$0 | ||
726 | "#, | ||
727 | expect![[r#" | ||
728 | md bar | ||
729 | ma foo!(…) #[macro_export] macro_rules! foo | ||
730 | "#]], | ||
731 | ) | ||
732 | } | ||
733 | |||
734 | #[test] | ||
735 | fn test_super_super_completion() { | 717 | fn test_super_super_completion() { |
736 | check( | 718 | check( |
737 | r#" | 719 | r#" |
diff --git a/crates/ide_completion/src/completions/snippet.rs b/crates/ide_completion/src/completions/snippet.rs index b9862de67..5560f1acf 100644 --- a/crates/ide_completion/src/completions/snippet.rs +++ b/crates/ide_completion/src/completions/snippet.rs | |||
@@ -105,21 +105,4 @@ mod tests { | |||
105 | check(r#"fn foo(x: i32) { ::foo$0 }"#, expect![[""]]); | 105 | check(r#"fn foo(x: i32) { ::foo$0 }"#, expect![[""]]); |
106 | check(r#"fn foo(x: i32) { ::$0 }"#, expect![[""]]); | 106 | check(r#"fn foo(x: i32) { ::$0 }"#, expect![[""]]); |
107 | } | 107 | } |
108 | |||
109 | #[test] | ||
110 | fn completes_snippets_in_items() { | ||
111 | check( | ||
112 | r#" | ||
113 | #[cfg(test)] | ||
114 | mod tests { | ||
115 | $0 | ||
116 | } | ||
117 | "#, | ||
118 | expect![[r#" | ||
119 | sn tmod (Test module) | ||
120 | sn tfn (Test function) | ||
121 | sn macro_rules | ||
122 | "#]], | ||
123 | ) | ||
124 | } | ||
125 | } | 108 | } |
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs index b5af1c810..2c623bf7a 100644 --- a/crates/ide_completion/src/completions/unqualified_path.rs +++ b/crates/ide_completion/src/completions/unqualified_path.rs | |||
@@ -40,7 +40,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
40 | if let Some(hir::Adt::Enum(e)) = | 40 | if let Some(hir::Adt::Enum(e)) = |
41 | ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) | 41 | ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) |
42 | { | 42 | { |
43 | super::complete_enum_variants(acc, ctx, e, |acc, ctx, variant, path| { | 43 | super::enum_variants_with_paths(acc, ctx, e, |acc, ctx, variant, path| { |
44 | acc.add_qualified_enum_variant(ctx, variant, path) | 44 | acc.add_qualified_enum_variant(ctx, variant, path) |
45 | }); | 45 | }); |
46 | } | 46 | } |
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs index 18983aa01..be6442426 100644 --- a/crates/ide_completion/src/lib.rs +++ b/crates/ide_completion/src/lib.rs | |||
@@ -1,14 +1,16 @@ | |||
1 | //! `completions` crate provides utilities for generating completions of user input. | 1 | //! `completions` crate provides utilities for generating completions of user input. |
2 | 2 | ||
3 | mod completions; | ||
3 | mod config; | 4 | mod config; |
4 | mod item; | ||
5 | mod context; | 5 | mod context; |
6 | mod item; | ||
6 | mod patterns; | 7 | mod patterns; |
7 | #[cfg(test)] | ||
8 | mod test_utils; | ||
9 | mod render; | 8 | mod render; |
10 | 9 | ||
11 | mod completions; | 10 | #[cfg(test)] |
11 | mod tests; | ||
12 | #[cfg(test)] | ||
13 | mod test_utils; | ||
12 | 14 | ||
13 | use completions::flyimport::position_for_import; | 15 | use completions::flyimport::position_for_import; |
14 | use ide_db::{ | 16 | use ide_db::{ |
@@ -141,6 +143,7 @@ pub fn completions( | |||
141 | let ctx = CompletionContext::new(db, position, config)?; | 143 | let ctx = CompletionContext::new(db, position, config)?; |
142 | 144 | ||
143 | if ctx.no_completion_required() { | 145 | if ctx.no_completion_required() { |
146 | cov_mark::hit!(no_completion_required); | ||
144 | // No work required here. | 147 | // No work required here. |
145 | return None; | 148 | return None; |
146 | } | 149 | } |
@@ -200,117 +203,3 @@ pub fn resolve_completion_edits( | |||
200 | 203 | ||
201 | ImportEdit { import, scope }.to_text_edit(config.insert_use).map(|edit| vec![edit]) | 204 | ImportEdit { import, scope }.to_text_edit(config.insert_use).map(|edit| vec![edit]) |
202 | } | 205 | } |
203 | |||
204 | #[cfg(test)] | ||
205 | mod tests { | ||
206 | use crate::test_utils::{self, TEST_CONFIG}; | ||
207 | |||
208 | struct DetailAndDocumentation<'a> { | ||
209 | detail: &'a str, | ||
210 | documentation: &'a str, | ||
211 | } | ||
212 | |||
213 | fn check_detail_and_documentation(ra_fixture: &str, expected: DetailAndDocumentation) { | ||
214 | let (db, position) = test_utils::position(ra_fixture); | ||
215 | let config = TEST_CONFIG; | ||
216 | let completions: Vec<_> = crate::completions(&db, &config, position).unwrap().into(); | ||
217 | for item in completions { | ||
218 | if item.detail() == Some(expected.detail) { | ||
219 | let opt = item.documentation(); | ||
220 | let doc = opt.as_ref().map(|it| it.as_str()); | ||
221 | assert_eq!(doc, Some(expected.documentation)); | ||
222 | return; | ||
223 | } | ||
224 | } | ||
225 | panic!("completion detail not found: {}", expected.detail) | ||
226 | } | ||
227 | |||
228 | fn check_no_completion(ra_fixture: &str) { | ||
229 | let (db, position) = test_utils::position(ra_fixture); | ||
230 | let config = TEST_CONFIG; | ||
231 | |||
232 | let completions: Option<Vec<String>> = crate::completions(&db, &config, position) | ||
233 | .and_then(|completions| { | ||
234 | let completions: Vec<_> = completions.into(); | ||
235 | if completions.is_empty() { | ||
236 | None | ||
237 | } else { | ||
238 | Some(completions) | ||
239 | } | ||
240 | }) | ||
241 | .map(|completions| { | ||
242 | completions.into_iter().map(|completion| format!("{:?}", completion)).collect() | ||
243 | }); | ||
244 | |||
245 | // `assert_eq` instead of `assert!(completions.is_none())` to get the list of completions if test will panic. | ||
246 | assert_eq!(completions, None, "Completions were generated, but weren't expected"); | ||
247 | } | ||
248 | |||
249 | #[test] | ||
250 | fn test_completion_detail_from_macro_generated_struct_fn_doc_attr() { | ||
251 | check_detail_and_documentation( | ||
252 | r#" | ||
253 | macro_rules! bar { | ||
254 | () => { | ||
255 | struct Bar; | ||
256 | impl Bar { | ||
257 | #[doc = "Do the foo"] | ||
258 | fn foo(&self) {} | ||
259 | } | ||
260 | } | ||
261 | } | ||
262 | |||
263 | bar!(); | ||
264 | |||
265 | fn foo() { | ||
266 | let bar = Bar; | ||
267 | bar.fo$0; | ||
268 | } | ||
269 | "#, | ||
270 | DetailAndDocumentation { detail: "fn(&self)", documentation: "Do the foo" }, | ||
271 | ); | ||
272 | } | ||
273 | |||
274 | #[test] | ||
275 | fn test_completion_detail_from_macro_generated_struct_fn_doc_comment() { | ||
276 | check_detail_and_documentation( | ||
277 | r#" | ||
278 | macro_rules! bar { | ||
279 | () => { | ||
280 | struct Bar; | ||
281 | impl Bar { | ||
282 | /// Do the foo | ||
283 | fn foo(&self) {} | ||
284 | } | ||
285 | } | ||
286 | } | ||
287 | |||
288 | bar!(); | ||
289 | |||
290 | fn foo() { | ||
291 | let bar = Bar; | ||
292 | bar.fo$0; | ||
293 | } | ||
294 | "#, | ||
295 | DetailAndDocumentation { detail: "fn(&self)", documentation: "Do the foo" }, | ||
296 | ); | ||
297 | } | ||
298 | |||
299 | #[test] | ||
300 | fn test_no_completions_required() { | ||
301 | // There must be no hint for 'in' keyword. | ||
302 | check_no_completion(r#"fn foo() { for i i$0 }"#); | ||
303 | // After 'in' keyword hints may be spawned. | ||
304 | check_detail_and_documentation( | ||
305 | r#" | ||
306 | /// Do the foo | ||
307 | fn foo() -> &'static str { "foo" } | ||
308 | |||
309 | fn bar() { | ||
310 | for c in fo$0 | ||
311 | } | ||
312 | "#, | ||
313 | DetailAndDocumentation { detail: "fn() -> &str", documentation: "Do the foo" }, | ||
314 | ); | ||
315 | } | ||
316 | } | ||
diff --git a/crates/ide_completion/src/tests.rs b/crates/ide_completion/src/tests.rs new file mode 100644 index 000000000..4485a908e --- /dev/null +++ b/crates/ide_completion/src/tests.rs | |||
@@ -0,0 +1,64 @@ | |||
1 | mod item_list; | ||
2 | |||
3 | use expect_test::Expect; | ||
4 | use stdx::format_to; | ||
5 | |||
6 | use crate::{ | ||
7 | test_utils::{self, get_all_items, TEST_CONFIG}, | ||
8 | CompletionConfig, CompletionItem, | ||
9 | }; | ||
10 | |||
11 | fn completion_list(code: &str) -> String { | ||
12 | completion_list_with_config(TEST_CONFIG, code) | ||
13 | } | ||
14 | |||
15 | fn completion_list_with_config(config: CompletionConfig, code: &str) -> String { | ||
16 | fn monospace_width(s: &str) -> usize { | ||
17 | s.chars().count() | ||
18 | } | ||
19 | |||
20 | let kind_completions: Vec<CompletionItem> = get_all_items(config, code).into_iter().collect(); | ||
21 | let label_width = kind_completions | ||
22 | .iter() | ||
23 | .map(|it| monospace_width(it.label())) | ||
24 | .max() | ||
25 | .unwrap_or_default() | ||
26 | .min(16); | ||
27 | kind_completions | ||
28 | .into_iter() | ||
29 | .map(|it| { | ||
30 | let tag = it.kind().unwrap().tag(); | ||
31 | let var_name = format!("{} {}", tag, it.label()); | ||
32 | let mut buf = var_name; | ||
33 | if let Some(detail) = it.detail() { | ||
34 | let width = label_width.saturating_sub(monospace_width(it.label())); | ||
35 | format_to!(buf, "{:width$} {}", "", detail, width = width); | ||
36 | } | ||
37 | if it.deprecated() { | ||
38 | format_to!(buf, " DEPRECATED"); | ||
39 | } | ||
40 | format_to!(buf, "\n"); | ||
41 | buf | ||
42 | }) | ||
43 | .collect() | ||
44 | } | ||
45 | |||
46 | fn check(ra_fixture: &str, expect: Expect) { | ||
47 | let actual = completion_list(ra_fixture); | ||
48 | expect.assert_eq(&actual) | ||
49 | } | ||
50 | |||
51 | fn check_no_completion(ra_fixture: &str) { | ||
52 | let (db, position) = test_utils::position(ra_fixture); | ||
53 | |||
54 | assert!( | ||
55 | crate::completions(&db, &TEST_CONFIG, position).is_none(), | ||
56 | "Completions were generated, but weren't expected" | ||
57 | ); | ||
58 | } | ||
59 | |||
60 | #[test] | ||
61 | fn test_no_completions_required() { | ||
62 | cov_mark::check!(no_completion_required); | ||
63 | check_no_completion(r#"fn foo() { for i i$0 }"#); | ||
64 | } | ||
diff --git a/crates/ide_completion/src/tests/item_list.rs b/crates/ide_completion/src/tests/item_list.rs new file mode 100644 index 000000000..bd060a632 --- /dev/null +++ b/crates/ide_completion/src/tests/item_list.rs | |||
@@ -0,0 +1,172 @@ | |||
1 | use expect_test::expect; | ||
2 | |||
3 | use crate::tests::check; | ||
4 | |||
5 | #[test] | ||
6 | fn in_mod_item_list() { | ||
7 | check( | ||
8 | r#"mod tests { | ||
9 | $0 | ||
10 | } | ||
11 | "#, | ||
12 | expect![[r#" | ||
13 | kw pub(crate) | ||
14 | kw pub | ||
15 | kw unsafe | ||
16 | kw fn | ||
17 | kw const | ||
18 | kw type | ||
19 | kw use | ||
20 | kw impl | ||
21 | kw trait | ||
22 | kw static | ||
23 | kw extern | ||
24 | kw mod | ||
25 | kw enum | ||
26 | kw struct | ||
27 | kw union | ||
28 | sn tmod (Test module) | ||
29 | sn tfn (Test function) | ||
30 | sn macro_rules | ||
31 | "#]], | ||
32 | ) | ||
33 | } | ||
34 | |||
35 | #[test] | ||
36 | fn in_source_file_item_list() { | ||
37 | check( | ||
38 | r#" | ||
39 | enum Enum { Variant } | ||
40 | struct MyStruct {} | ||
41 | #[macro_export] | ||
42 | macro_rules! foo {} | ||
43 | mod bar {} | ||
44 | const CONST: () = (); | ||
45 | |||
46 | $0"#, | ||
47 | expect![[r##" | ||
48 | kw pub(crate) | ||
49 | kw pub | ||
50 | kw unsafe | ||
51 | kw fn | ||
52 | kw const | ||
53 | kw type | ||
54 | kw use | ||
55 | kw impl | ||
56 | kw trait | ||
57 | kw static | ||
58 | kw extern | ||
59 | kw mod | ||
60 | kw enum | ||
61 | kw struct | ||
62 | kw union | ||
63 | sn tmod (Test module) | ||
64 | sn tfn (Test function) | ||
65 | sn macro_rules | ||
66 | md bar | ||
67 | ma foo!(…) #[macro_export] macro_rules! foo | ||
68 | ma foo!(…) #[macro_export] macro_rules! foo | ||
69 | "##]], | ||
70 | ) | ||
71 | } | ||
72 | |||
73 | #[test] | ||
74 | fn in_qualified_path() { | ||
75 | check( | ||
76 | r#" | ||
77 | enum Enum { Variant } | ||
78 | struct MyStruct {} | ||
79 | #[macro_export] | ||
80 | macro_rules! foo {} | ||
81 | mod bar {} | ||
82 | const CONST: () = (); | ||
83 | |||
84 | crate::$0"#, | ||
85 | expect![[r##" | ||
86 | kw pub(crate) | ||
87 | kw pub | ||
88 | kw unsafe | ||
89 | kw fn | ||
90 | kw const | ||
91 | kw type | ||
92 | kw use | ||
93 | kw impl | ||
94 | kw trait | ||
95 | kw static | ||
96 | kw extern | ||
97 | kw mod | ||
98 | kw enum | ||
99 | kw struct | ||
100 | kw union | ||
101 | sn tmod (Test module) | ||
102 | sn tfn (Test function) | ||
103 | sn macro_rules | ||
104 | md bar | ||
105 | ma foo!(…) #[macro_export] macro_rules! foo | ||
106 | "##]], | ||
107 | ) | ||
108 | } | ||
109 | |||
110 | #[test] | ||
111 | fn after_unsafe_token() { | ||
112 | check( | ||
113 | r#" | ||
114 | enum Enum { Variant } | ||
115 | struct MyStruct {} | ||
116 | #[macro_export] | ||
117 | macro_rules! foo {} | ||
118 | mod bar {} | ||
119 | const CONST: () = (); | ||
120 | |||
121 | unsafe $0"#, | ||
122 | expect![[r##" | ||
123 | kw fn | ||
124 | kw trait | ||
125 | kw impl | ||
126 | sn tmod (Test module) | ||
127 | sn tfn (Test function) | ||
128 | sn macro_rules | ||
129 | md bar | ||
130 | ma foo!(…) #[macro_export] macro_rules! foo | ||
131 | ma foo!(…) #[macro_export] macro_rules! foo | ||
132 | "##]], | ||
133 | ); | ||
134 | } | ||
135 | |||
136 | #[test] | ||
137 | fn after_visibility() { | ||
138 | check( | ||
139 | r#" | ||
140 | enum Enum { Variant } | ||
141 | struct MyStruct {} | ||
142 | #[macro_export] | ||
143 | macro_rules! foo {} | ||
144 | mod bar {} | ||
145 | const CONST: () = (); | ||
146 | |||
147 | pub $0"#, | ||
148 | expect![[r##" | ||
149 | kw pub(crate) | ||
150 | kw pub | ||
151 | kw unsafe | ||
152 | kw fn | ||
153 | kw const | ||
154 | kw type | ||
155 | kw use | ||
156 | kw impl | ||
157 | kw trait | ||
158 | kw static | ||
159 | kw extern | ||
160 | kw mod | ||
161 | kw enum | ||
162 | kw struct | ||
163 | kw union | ||
164 | sn tmod (Test module) | ||
165 | sn tfn (Test function) | ||
166 | sn macro_rules | ||
167 | md bar | ||
168 | ma foo!(…) #[macro_export] macro_rules! foo | ||
169 | ma foo!(…) #[macro_export] macro_rules! foo | ||
170 | "##]], | ||
171 | ); | ||
172 | } | ||