aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_completion
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_completion')
-rw-r--r--crates/ide_completion/src/completions/flyimport.rs93
-rw-r--r--crates/ide_completion/src/completions/lifetime.rs106
-rw-r--r--crates/ide_completion/src/context.rs26
-rw-r--r--crates/ide_completion/src/lib.rs28
-rw-r--r--crates/ide_completion/src/render.rs1
5 files changed, 234 insertions, 20 deletions
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs
index 08df2df3f..1ad017198 100644
--- a/crates/ide_completion/src/completions/flyimport.rs
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -1,8 +1,10 @@
1//! Feature: completion with imports-on-the-fly 1//! Feature: completion with imports-on-the-fly
2//! 2//!
3//! When completing names in the current scope, proposes additional imports from other modules or crates, 3//! When completing names in the current scope, proposes additional imports from other modules or crates,
4//! if they can be qualified in the scope and their name contains all symbols from the completion input 4//! if they can be qualified in the scope and their name contains all symbols from the completion input.
5//! (case-insensitive, in any order or places). 5//!
6//! To be considered applicable, the name must contain all input symbols in the given order, not necessarily adjacent.
7//! If any input symbol is not lowercased, the name must contain all symbols in exact case; otherwise the contaning is checked case-insensitively.
6//! 8//!
7//! ``` 9//! ```
8//! fn main() { 10//! fn main() {
@@ -942,9 +944,94 @@ mod foo {
942} 944}
943 945
944fn main() { 946fn main() {
945 bar::Ass$0 947 bar::ASS$0
946}"#, 948}"#,
947 expect![[]], 949 expect![[]],
948 ) 950 )
949 } 951 }
952
953 #[test]
954 fn unqualified_assoc_items_are_omitted() {
955 check(
956 r#"
957mod something {
958 pub trait BaseTrait {
959 fn test_function() -> i32;
960 }
961
962 pub struct Item1;
963 pub struct Item2;
964
965 impl BaseTrait for Item1 {
966 fn test_function() -> i32 {
967 1
968 }
969 }
970
971 impl BaseTrait for Item2 {
972 fn test_function() -> i32 {
973 2
974 }
975 }
976}
977
978fn main() {
979 test_f$0
980}"#,
981 expect![[]],
982 )
983 }
984
985 #[test]
986 fn case_matters() {
987 check(
988 r#"
989mod foo {
990 pub const TEST_CONST: usize = 3;
991 pub fn test_function() -> i32 {
992 4
993 }
994}
995
996fn main() {
997 TE$0
998}"#,
999 expect![[r#"
1000 ct foo::TEST_CONST
1001 "#]],
1002 );
1003
1004 check(
1005 r#"
1006mod foo {
1007 pub const TEST_CONST: usize = 3;
1008 pub fn test_function() -> i32 {
1009 4
1010 }
1011}
1012
1013fn main() {
1014 te$0
1015}"#,
1016 expect![[r#"
1017 ct foo::TEST_CONST
1018 fn test_function() (foo::test_function) fn() -> i32
1019 "#]],
1020 );
1021
1022 check(
1023 r#"
1024mod foo {
1025 pub const TEST_CONST: usize = 3;
1026 pub fn test_function() -> i32 {
1027 4
1028 }
1029}
1030
1031fn main() {
1032 Te$0
1033}"#,
1034 expect![[]],
1035 );
1036 }
950} 1037}
diff --git a/crates/ide_completion/src/completions/lifetime.rs b/crates/ide_completion/src/completions/lifetime.rs
index 74eb23360..628c1fb9b 100644
--- a/crates/ide_completion/src/completions/lifetime.rs
+++ b/crates/ide_completion/src/completions/lifetime.rs
@@ -1,4 +1,4 @@
1//! Completes lifetimes. 1//! Completes lifetimes and labels.
2use hir::ScopeDef; 2use hir::ScopeDef;
3 3
4use crate::{completions::Completions, context::CompletionContext}; 4use crate::{completions::Completions, context::CompletionContext};
@@ -29,6 +29,18 @@ pub(crate) fn complete_lifetime(acc: &mut Completions, ctx: &CompletionContext)
29 } 29 }
30} 30}
31 31
32/// Completes labels.
33pub(crate) fn complete_label(acc: &mut Completions, ctx: &CompletionContext) {
34 if !ctx.is_label_ref {
35 return;
36 }
37 ctx.scope.process_all_names(&mut |name, res| {
38 if let ScopeDef::Label(_) = res {
39 acc.add_resolution(ctx, name.to_string(), &res);
40 }
41 });
42}
43
32#[cfg(test)] 44#[cfg(test)]
33mod tests { 45mod tests {
34 use expect_test::{expect, Expect}; 46 use expect_test::{expect, Expect};
@@ -178,4 +190,96 @@ fn foo<'footime, 'lifetime: 'a$0>() {}
178 "#]], 190 "#]],
179 ); 191 );
180 } 192 }
193
194 #[test]
195 fn complete_label_in_loop() {
196 check(
197 r#"
198fn foo() {
199 'foop: loop {
200 break '$0
201 }
202}
203"#,
204 expect![[r#"
205 lb 'foop
206 "#]],
207 );
208 check(
209 r#"
210fn foo() {
211 'foop: loop {
212 continue '$0
213 }
214}
215"#,
216 expect![[r#"
217 lb 'foop
218 "#]],
219 );
220 }
221
222 #[test]
223 fn complete_label_in_block_nested() {
224 check(
225 r#"
226fn foo() {
227 'foop: {
228 'baap: {
229 break '$0
230 }
231 }
232}
233"#,
234 expect![[r#"
235 lb 'baap
236 lb 'foop
237 "#]],
238 );
239 }
240
241 #[test]
242 fn complete_label_in_loop_with_value() {
243 check(
244 r#"
245fn foo() {
246 'foop: loop {
247 break '$0 i32;
248 }
249}
250"#,
251 expect![[r#"
252 lb 'foop
253 "#]],
254 );
255 }
256
257 #[test]
258 fn complete_label_in_while_cond() {
259 check(
260 r#"
261fn foo() {
262 'outer: while { 'inner: loop { break '$0 } } {}
263}
264"#,
265 expect![[r#"
266 lb 'inner
267 lb 'outer
268 "#]],
269 );
270 }
271
272 #[test]
273 fn complete_label_in_for_iterable() {
274 check(
275 r#"
276fn foo() {
277 'outer: for _ in [{ 'inner: loop { break '$0 } }] {}
278}
279"#,
280 expect![[r#"
281 lb 'inner
282 "#]],
283 );
284 }
181} 285}
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index 4c2b31084..67e2d6f6c 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -53,6 +53,7 @@ pub(crate) struct CompletionContext<'a> {
53 /// FIXME: `ActiveParameter` is string-based, which is very very wrong 53 /// FIXME: `ActiveParameter` is string-based, which is very very wrong
54 pub(super) active_parameter: Option<ActiveParameter>, 54 pub(super) active_parameter: Option<ActiveParameter>,
55 pub(super) is_param: bool, 55 pub(super) is_param: bool,
56 pub(super) is_label_ref: bool,
56 /// If a name-binding or reference to a const in a pattern. 57 /// If a name-binding or reference to a const in a pattern.
57 /// Irrefutable patterns (like let) are excluded. 58 /// Irrefutable patterns (like let) are excluded.
58 pub(super) is_pat_binding_or_const: bool, 59 pub(super) is_pat_binding_or_const: bool,
@@ -155,6 +156,7 @@ impl<'a> CompletionContext<'a> {
155 record_field_syntax: None, 156 record_field_syntax: None,
156 impl_def: None, 157 impl_def: None,
157 active_parameter: ActiveParameter::at(db, position), 158 active_parameter: ActiveParameter::at(db, position),
159 is_label_ref: false,
158 is_param: false, 160 is_param: false,
159 is_pat_binding_or_const: false, 161 is_pat_binding_or_const: false,
160 is_irrefutable_pat_binding: false, 162 is_irrefutable_pat_binding: false,
@@ -468,12 +470,24 @@ impl<'a> CompletionContext<'a> {
468 ) { 470 ) {
469 self.lifetime_syntax = 471 self.lifetime_syntax =
470 find_node_at_offset(original_file, lifetime.syntax().text_range().start()); 472 find_node_at_offset(original_file, lifetime.syntax().text_range().start());
471 if lifetime.syntax().parent().map_or(false, |p| p.kind() != syntax::SyntaxKind::ERROR) { 473 if let Some(parent) = lifetime.syntax().parent() {
472 self.lifetime_allowed = true; 474 if parent.kind() == syntax::SyntaxKind::ERROR {
473 } 475 return;
474 if let Some(_) = lifetime.syntax().parent().and_then(ast::LifetimeParam::cast) { 476 }
475 self.lifetime_param_syntax = 477
476 self.sema.find_node_at_offset_with_macros(original_file, offset); 478 match_ast! {
479 match parent {
480 ast::LifetimeParam(_it) => {
481 self.lifetime_allowed = true;
482 self.lifetime_param_syntax =
483 self.sema.find_node_at_offset_with_macros(original_file, offset);
484 },
485 ast::BreakExpr(_it) => self.is_label_ref = true,
486 ast::ContinueExpr(_it) => self.is_label_ref = true,
487 ast::Label(_it) => (),
488 _ => self.lifetime_allowed = true,
489 }
490 }
477 } 491 }
478 } 492 }
479 493
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs
index 7a0eb6a96..5ac1cb48d 100644
--- a/crates/ide_completion/src/lib.rs
+++ b/crates/ide_completion/src/lib.rs
@@ -14,7 +14,10 @@ mod completions;
14use completions::flyimport::position_for_import; 14use completions::flyimport::position_for_import;
15use ide_db::{ 15use ide_db::{
16 base_db::FilePosition, 16 base_db::FilePosition,
17 helpers::{import_assets::LocatedImport, insert_use::ImportScope}, 17 helpers::{
18 import_assets::{LocatedImport, NameToImport},
19 insert_use::ImportScope,
20 },
18 items_locator, RootDatabase, 21 items_locator, RootDatabase,
19}; 22};
20use text_edit::TextEdit; 23use text_edit::TextEdit;
@@ -131,6 +134,7 @@ pub fn completions(
131 completions::mod_::complete_mod(&mut acc, &ctx); 134 completions::mod_::complete_mod(&mut acc, &ctx);
132 completions::flyimport::import_on_the_fly(&mut acc, &ctx); 135 completions::flyimport::import_on_the_fly(&mut acc, &ctx);
133 completions::lifetime::complete_lifetime(&mut acc, &ctx); 136 completions::lifetime::complete_lifetime(&mut acc, &ctx);
137 completions::lifetime::complete_label(&mut acc, &ctx);
134 138
135 Some(acc) 139 Some(acc)
136} 140}
@@ -150,15 +154,19 @@ pub fn resolve_completion_edits(
150 let current_module = ctx.sema.scope(position_for_import).module()?; 154 let current_module = ctx.sema.scope(position_for_import).module()?;
151 let current_crate = current_module.krate(); 155 let current_crate = current_module.krate();
152 156
153 let (import_path, item_to_import) = 157 let (import_path, item_to_import) = items_locator::items_with_name(
154 items_locator::with_exact_name(&ctx.sema, current_crate, imported_name) 158 &ctx.sema,
155 .into_iter() 159 current_crate,
156 .filter_map(|candidate| { 160 NameToImport::Exact(imported_name),
157 current_module 161 items_locator::AssocItemSearch::Include,
158 .find_use_path_prefixed(db, candidate, config.insert_use.prefix_kind) 162 Some(items_locator::DEFAULT_QUERY_SEARCH_LIMIT),
159 .zip(Some(candidate)) 163 )
160 }) 164 .filter_map(|candidate| {
161 .find(|(mod_path, _)| mod_path.to_string() == full_import_path)?; 165 current_module
166 .find_use_path_prefixed(db, candidate, config.insert_use.prefix_kind)
167 .zip(Some(candidate))
168 })
169 .find(|(mod_path, _)| mod_path.to_string() == full_import_path)?;
162 let import = 170 let import =
163 LocatedImport::new(import_path.clone(), item_to_import, item_to_import, Some(import_path)); 171 LocatedImport::new(import_path.clone(), item_to_import, item_to_import, Some(import_path));
164 172
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index 2b6e9ebd1..23e00aa47 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -219,6 +219,7 @@ impl<'a> Render<'a> {
219 hir::GenericParam::ConstParam(_) => SymbolKind::ConstParam, 219 hir::GenericParam::ConstParam(_) => SymbolKind::ConstParam,
220 }), 220 }),
221 ScopeDef::Local(..) => CompletionItemKind::SymbolKind(SymbolKind::Local), 221 ScopeDef::Local(..) => CompletionItemKind::SymbolKind(SymbolKind::Local),
222 ScopeDef::Label(..) => CompletionItemKind::SymbolKind(SymbolKind::Label),
222 ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => { 223 ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => {
223 CompletionItemKind::SymbolKind(SymbolKind::SelfParam) 224 CompletionItemKind::SymbolKind(SymbolKind::SelfParam)
224 } 225 }