diff options
Diffstat (limited to 'crates/ide_completion')
-rw-r--r-- | crates/ide_completion/src/completions/attribute.rs | 19 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/attribute/derive.rs | 80 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/attribute/lint.rs | 12 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/dot.rs | 6 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/flyimport.rs | 6 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/keyword.rs | 11 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/postfix.rs | 2 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/postfix/format_like.rs | 18 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/qualified_path.rs | 4 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/snippet.rs | 4 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/unqualified_path.rs | 2 | ||||
-rw-r--r-- | crates/ide_completion/src/context.rs | 120 | ||||
-rw-r--r-- | crates/ide_completion/src/patterns.rs | 4 | ||||
-rw-r--r-- | crates/ide_completion/src/render.rs | 8 | ||||
-rw-r--r-- | crates/ide_completion/src/render/builder_ext.rs | 12 | ||||
-rw-r--r-- | crates/ide_completion/src/render/macro_.rs | 4 |
16 files changed, 188 insertions, 124 deletions
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs index f80d7eec3..d3392100d 100644 --- a/crates/ide_completion/src/completions/attribute.rs +++ b/crates/ide_completion/src/completions/attribute.rs | |||
@@ -3,6 +3,7 @@ | |||
3 | //! This module uses a bit of static metadata to provide completions | 3 | //! This module uses a bit of static metadata to provide completions |
4 | //! for built-in attributes. | 4 | //! for built-in attributes. |
5 | 5 | ||
6 | use hir::HasAttrs; | ||
6 | use ide_db::helpers::generated_lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES}; | 7 | use ide_db::helpers::generated_lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES}; |
7 | use once_cell::sync::Lazy; | 8 | use once_cell::sync::Lazy; |
8 | use rustc_hash::{FxHashMap, FxHashSet}; | 9 | use rustc_hash::{FxHashMap, FxHashSet}; |
@@ -81,6 +82,24 @@ fn complete_new_attribute(acc: &mut Completions, ctx: &CompletionContext, attrib | |||
81 | None if is_inner => ATTRIBUTES.iter().for_each(add_completion), | 82 | None if is_inner => ATTRIBUTES.iter().for_each(add_completion), |
82 | None => ATTRIBUTES.iter().filter(|compl| !compl.prefer_inner).for_each(add_completion), | 83 | None => ATTRIBUTES.iter().filter(|compl| !compl.prefer_inner).for_each(add_completion), |
83 | } | 84 | } |
85 | |||
86 | // FIXME: write a test for this when we can | ||
87 | ctx.scope.process_all_names(&mut |name, scope_def| { | ||
88 | if let hir::ScopeDef::MacroDef(mac) = scope_def { | ||
89 | if mac.kind() == hir::MacroKind::Attr { | ||
90 | let mut item = CompletionItem::new( | ||
91 | CompletionKind::Attribute, | ||
92 | ctx.source_range(), | ||
93 | name.to_string(), | ||
94 | ); | ||
95 | item.kind(CompletionItemKind::Attribute); | ||
96 | if let Some(docs) = mac.docs(ctx.sema.db) { | ||
97 | item.documentation(docs); | ||
98 | } | ||
99 | acc.add(item.build()); | ||
100 | } | ||
101 | } | ||
102 | }); | ||
84 | } | 103 | } |
85 | 104 | ||
86 | struct AttrCompletion { | 105 | struct AttrCompletion { |
diff --git a/crates/ide_completion/src/completions/attribute/derive.rs b/crates/ide_completion/src/completions/attribute/derive.rs index 0bc3eab98..d526824fb 100644 --- a/crates/ide_completion/src/completions/attribute/derive.rs +++ b/crates/ide_completion/src/completions/attribute/derive.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | //! Completion for derives | 1 | //! Completion for derives |
2 | use hir::HasAttrs; | ||
2 | use itertools::Itertools; | 3 | use itertools::Itertools; |
3 | use rustc_hash::FxHashSet; | 4 | use rustc_hash::FxHashMap; |
4 | use syntax::ast; | 5 | use syntax::ast; |
5 | 6 | ||
6 | use crate::{ | 7 | use crate::{ |
@@ -15,66 +16,64 @@ pub(super) fn complete_derive( | |||
15 | derive_input: ast::TokenTree, | 16 | derive_input: ast::TokenTree, |
16 | ) { | 17 | ) { |
17 | if let Some(existing_derives) = super::parse_comma_sep_input(derive_input) { | 18 | if let Some(existing_derives) = super::parse_comma_sep_input(derive_input) { |
18 | for derive_completion in DEFAULT_DERIVE_COMPLETIONS | 19 | for (derive, docs) in get_derive_names_in_scope(ctx) { |
19 | .iter() | 20 | let (label, lookup) = if let Some(derive_completion) = DEFAULT_DERIVE_COMPLETIONS |
20 | .filter(|completion| !existing_derives.contains(completion.label)) | 21 | .iter() |
21 | { | 22 | .find(|derive_completion| derive_completion.label == derive) |
22 | let mut components = vec![derive_completion.label]; | 23 | { |
23 | components.extend( | 24 | let mut components = vec![derive_completion.label]; |
24 | derive_completion | 25 | components.extend( |
25 | .dependencies | 26 | derive_completion |
26 | .iter() | 27 | .dependencies |
27 | .filter(|&&dependency| !existing_derives.contains(dependency)), | 28 | .iter() |
28 | ); | 29 | .filter(|&&dependency| !existing_derives.contains(dependency)), |
29 | let lookup = components.join(", "); | 30 | ); |
30 | let label = components.iter().rev().join(", "); | 31 | let lookup = components.join(", "); |
32 | let label = components.iter().rev().join(", "); | ||
33 | (label, Some(lookup)) | ||
34 | } else { | ||
35 | (derive, None) | ||
36 | }; | ||
31 | let mut item = | 37 | let mut item = |
32 | CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label); | 38 | CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label); |
33 | item.lookup_by(lookup).kind(CompletionItemKind::Attribute); | ||
34 | item.add_to(acc); | ||
35 | } | ||
36 | |||
37 | for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) { | ||
38 | let mut item = CompletionItem::new( | ||
39 | CompletionKind::Attribute, | ||
40 | ctx.source_range(), | ||
41 | custom_derive_name, | ||
42 | ); | ||
43 | item.kind(CompletionItemKind::Attribute); | 39 | item.kind(CompletionItemKind::Attribute); |
40 | if let Some(docs) = docs { | ||
41 | item.documentation(docs); | ||
42 | } | ||
43 | if let Some(lookup) = lookup { | ||
44 | item.lookup_by(lookup); | ||
45 | } | ||
44 | item.add_to(acc); | 46 | item.add_to(acc); |
45 | } | 47 | } |
46 | } | 48 | } |
47 | } | 49 | } |
48 | 50 | ||
49 | fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> { | 51 | fn get_derive_names_in_scope( |
50 | let mut result = FxHashSet::default(); | 52 | ctx: &CompletionContext, |
53 | ) -> FxHashMap<String, Option<hir::Documentation>> { | ||
54 | let mut result = FxHashMap::default(); | ||
51 | ctx.scope.process_all_names(&mut |name, scope_def| { | 55 | ctx.scope.process_all_names(&mut |name, scope_def| { |
52 | if let hir::ScopeDef::MacroDef(mac) = scope_def { | 56 | if let hir::ScopeDef::MacroDef(mac) = scope_def { |
53 | if mac.kind() == hir::MacroKind::Derive { | 57 | if mac.kind() == hir::MacroKind::Derive { |
54 | result.insert(name.to_string()); | 58 | result.insert(name.to_string(), mac.docs(ctx.db)); |
55 | } | 59 | } |
56 | } | 60 | } |
57 | }); | 61 | }); |
58 | result | 62 | result |
59 | } | 63 | } |
60 | 64 | ||
61 | struct DeriveCompletion { | 65 | struct DeriveDependencies { |
62 | label: &'static str, | 66 | label: &'static str, |
63 | dependencies: &'static [&'static str], | 67 | dependencies: &'static [&'static str], |
64 | } | 68 | } |
65 | 69 | ||
66 | /// Standard Rust derives and the information about their dependencies | 70 | /// Standard Rust derives that have dependencies |
67 | /// (the dependencies are needed so that the main derive don't break the compilation when added) | 71 | /// (the dependencies are needed so that the main derive don't break the compilation when added) |
68 | const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[ | 72 | const DEFAULT_DERIVE_COMPLETIONS: &[DeriveDependencies] = &[ |
69 | DeriveCompletion { label: "Clone", dependencies: &[] }, | 73 | DeriveDependencies { label: "Copy", dependencies: &["Clone"] }, |
70 | DeriveCompletion { label: "Copy", dependencies: &["Clone"] }, | 74 | DeriveDependencies { label: "Eq", dependencies: &["PartialEq"] }, |
71 | DeriveCompletion { label: "Debug", dependencies: &[] }, | 75 | DeriveDependencies { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] }, |
72 | DeriveCompletion { label: "Default", dependencies: &[] }, | 76 | DeriveDependencies { label: "PartialOrd", dependencies: &["PartialEq"] }, |
73 | DeriveCompletion { label: "Hash", dependencies: &[] }, | ||
74 | DeriveCompletion { label: "PartialEq", dependencies: &[] }, | ||
75 | DeriveCompletion { label: "Eq", dependencies: &["PartialEq"] }, | ||
76 | DeriveCompletion { label: "PartialOrd", dependencies: &["PartialEq"] }, | ||
77 | DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] }, | ||
78 | ]; | 77 | ]; |
79 | 78 | ||
80 | #[cfg(test)] | 79 | #[cfg(test)] |
@@ -94,6 +93,7 @@ mod tests { | |||
94 | } | 93 | } |
95 | 94 | ||
96 | #[test] | 95 | #[test] |
96 | #[ignore] // FIXME: Fixtures cant test proc-macros/derives yet as we cant specify them in fixtures | ||
97 | fn empty_derive() { | 97 | fn empty_derive() { |
98 | check( | 98 | check( |
99 | r#"#[derive($0)] struct Test;"#, | 99 | r#"#[derive($0)] struct Test;"#, |
@@ -112,6 +112,7 @@ mod tests { | |||
112 | } | 112 | } |
113 | 113 | ||
114 | #[test] | 114 | #[test] |
115 | #[ignore] // FIXME: Fixtures cant test proc-macros/derives yet as we cant specify them in fixtures | ||
115 | fn derive_with_input() { | 116 | fn derive_with_input() { |
116 | check( | 117 | check( |
117 | r#"#[derive(serde::Serialize, PartialEq, $0)] struct Test;"#, | 118 | r#"#[derive(serde::Serialize, PartialEq, $0)] struct Test;"#, |
@@ -129,6 +130,7 @@ mod tests { | |||
129 | } | 130 | } |
130 | 131 | ||
131 | #[test] | 132 | #[test] |
133 | #[ignore] // FIXME: Fixtures cant test proc-macros/derives yet as we cant specify them in fixtures | ||
132 | fn derive_with_input2() { | 134 | fn derive_with_input2() { |
133 | check( | 135 | check( |
134 | r#"#[derive($0 serde::Serialize, PartialEq)] struct Test;"#, | 136 | r#"#[derive($0 serde::Serialize, PartialEq)] struct Test;"#, |
diff --git a/crates/ide_completion/src/completions/attribute/lint.rs b/crates/ide_completion/src/completions/attribute/lint.rs index b486c9093..ca99e9759 100644 --- a/crates/ide_completion/src/completions/attribute/lint.rs +++ b/crates/ide_completion/src/completions/attribute/lint.rs | |||
@@ -24,7 +24,8 @@ pub(super) fn complete_lint( | |||
24 | ctx.source_range(), | 24 | ctx.source_range(), |
25 | lint_completion.label, | 25 | lint_completion.label, |
26 | ); | 26 | ); |
27 | item.kind(CompletionItemKind::Attribute).detail(lint_completion.description); | 27 | item.kind(CompletionItemKind::Attribute) |
28 | .documentation(hir::Documentation::new(lint_completion.description.to_owned())); | ||
28 | item.add_to(acc) | 29 | item.add_to(acc) |
29 | } | 30 | } |
30 | } | 31 | } |
@@ -61,4 +62,13 @@ mod tests { | |||
61 | r#"#[allow(keyword_idents, deprecated)] struct Test;"#, | 62 | r#"#[allow(keyword_idents, deprecated)] struct Test;"#, |
62 | ) | 63 | ) |
63 | } | 64 | } |
65 | |||
66 | #[test] | ||
67 | fn check_feature() { | ||
68 | check_edit( | ||
69 | "box_syntax", | ||
70 | r#"#[feature(box_$0)] struct Test;"#, | ||
71 | r#"#[feature(box_syntax)] struct Test;"#, | ||
72 | ) | ||
73 | } | ||
64 | } | 74 | } |
diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs index e0a7021fd..8ad57a069 100644 --- a/crates/ide_completion/src/completions/dot.rs +++ b/crates/ide_completion/src/completions/dot.rs | |||
@@ -4,7 +4,7 @@ use either::Either; | |||
4 | use hir::{HasVisibility, ScopeDef}; | 4 | use hir::{HasVisibility, ScopeDef}; |
5 | use rustc_hash::FxHashSet; | 5 | use rustc_hash::FxHashSet; |
6 | 6 | ||
7 | use crate::{context::CompletionContext, Completions}; | 7 | use crate::{context::CompletionContext, patterns::ImmediateLocation, Completions}; |
8 | 8 | ||
9 | /// Complete dot accesses, i.e. fields or methods. | 9 | /// Complete dot accesses, i.e. fields or methods. |
10 | pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { | 10 | pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { |
@@ -18,7 +18,7 @@ pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { | |||
18 | _ => return, | 18 | _ => return, |
19 | }; | 19 | }; |
20 | 20 | ||
21 | if ctx.is_call { | 21 | if matches!(ctx.completion_location, Some(ImmediateLocation::MethodCall { .. })) { |
22 | cov_mark::hit!(test_no_struct_field_completion_for_method_call); | 22 | cov_mark::hit!(test_no_struct_field_completion_for_method_call); |
23 | } else { | 23 | } else { |
24 | complete_fields(ctx, &receiver_ty, |field, ty| match field { | 24 | complete_fields(ctx, &receiver_ty, |field, ty| match field { |
@@ -33,7 +33,7 @@ fn complete_undotted_self(acc: &mut Completions, ctx: &CompletionContext) { | |||
33 | if !ctx.config.enable_self_on_the_fly { | 33 | if !ctx.config.enable_self_on_the_fly { |
34 | return; | 34 | return; |
35 | } | 35 | } |
36 | if !ctx.is_trivial_path || ctx.is_path_disallowed() { | 36 | if !ctx.is_trivial_path() || ctx.is_path_disallowed() { |
37 | return; | 37 | return; |
38 | } | 38 | } |
39 | ctx.scope.process_all_names(&mut |name, def| { | 39 | ctx.scope.process_all_names(&mut |name, def| { |
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs index d72bf13d3..7bf47bf75 100644 --- a/crates/ide_completion/src/completions/flyimport.rs +++ b/crates/ide_completion/src/completions/flyimport.rs | |||
@@ -161,13 +161,13 @@ pub(crate) fn position_for_import<'a>( | |||
161 | ) -> Option<&'a SyntaxNode> { | 161 | ) -> Option<&'a SyntaxNode> { |
162 | Some(match import_candidate { | 162 | Some(match import_candidate { |
163 | Some(ImportCandidate::Path(_)) => ctx.name_ref_syntax.as_ref()?.syntax(), | 163 | Some(ImportCandidate::Path(_)) => ctx.name_ref_syntax.as_ref()?.syntax(), |
164 | Some(ImportCandidate::TraitAssocItem(_)) => ctx.path_qual.as_ref()?.syntax(), | 164 | Some(ImportCandidate::TraitAssocItem(_)) => ctx.path_qual()?.syntax(), |
165 | Some(ImportCandidate::TraitMethod(_)) => ctx.dot_receiver()?.syntax(), | 165 | Some(ImportCandidate::TraitMethod(_)) => ctx.dot_receiver()?.syntax(), |
166 | None => ctx | 166 | None => ctx |
167 | .name_ref_syntax | 167 | .name_ref_syntax |
168 | .as_ref() | 168 | .as_ref() |
169 | .map(|name_ref| name_ref.syntax()) | 169 | .map(|name_ref| name_ref.syntax()) |
170 | .or_else(|| ctx.path_qual.as_ref().map(|path| path.syntax())) | 170 | .or_else(|| ctx.path_qual().map(|path| path.syntax())) |
171 | .or_else(|| ctx.dot_receiver().map(|expr| expr.syntax()))?, | 171 | .or_else(|| ctx.dot_receiver().map(|expr| expr.syntax()))?, |
172 | }) | 172 | }) |
173 | } | 173 | } |
@@ -190,7 +190,7 @@ fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAs | |||
190 | }; | 190 | }; |
191 | let assets_for_path = ImportAssets::for_fuzzy_path( | 191 | let assets_for_path = ImportAssets::for_fuzzy_path( |
192 | current_module, | 192 | current_module, |
193 | ctx.path_qual.clone(), | 193 | ctx.path_qual().cloned(), |
194 | fuzzy_name, | 194 | fuzzy_name, |
195 | &ctx.sema, | 195 | &ctx.sema, |
196 | approximate_node, | 196 | approximate_node, |
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs index 1a7a484a4..0ca97a0e4 100644 --- a/crates/ide_completion/src/completions/keyword.rs +++ b/crates/ide_completion/src/completions/keyword.rs | |||
@@ -19,11 +19,12 @@ pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC | |||
19 | }; | 19 | }; |
20 | 20 | ||
21 | if ctx.use_item_syntax.is_some() { | 21 | if ctx.use_item_syntax.is_some() { |
22 | if ctx.path_qual.is_none() { | 22 | let qual = ctx.path_qual(); |
23 | if qual.is_none() { | ||
23 | kw_completion("crate::").add_to(acc); | 24 | kw_completion("crate::").add_to(acc); |
24 | } | 25 | } |
25 | kw_completion("self").add_to(acc); | 26 | kw_completion("self").add_to(acc); |
26 | if iter::successors(ctx.path_qual.clone(), |p| p.qualifier()) | 27 | if iter::successors(qual.cloned(), |p| p.qualifier()) |
27 | .all(|p| p.segment().and_then(|s| s.super_token()).is_some()) | 28 | .all(|p| p.segment().and_then(|s| s.super_token()).is_some()) |
28 | { | 29 | { |
29 | kw_completion("super::").add_to(acc); | 30 | kw_completion("super::").add_to(acc); |
@@ -128,7 +129,7 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte | |||
128 | } | 129 | } |
129 | 130 | ||
130 | if ctx.in_loop_body { | 131 | if ctx.in_loop_body { |
131 | if ctx.can_be_stmt { | 132 | if ctx.can_be_stmt() { |
132 | add_keyword("continue", "continue;"); | 133 | add_keyword("continue", "continue;"); |
133 | add_keyword("break", "break;"); | 134 | add_keyword("break", "break;"); |
134 | } else { | 135 | } else { |
@@ -137,7 +138,7 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte | |||
137 | } | 138 | } |
138 | } | 139 | } |
139 | 140 | ||
140 | if !ctx.is_trivial_path { | 141 | if !ctx.is_trivial_path() { |
141 | return; | 142 | return; |
142 | } | 143 | } |
143 | let fn_def = match &ctx.function_def { | 144 | let fn_def = match &ctx.function_def { |
@@ -147,7 +148,7 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte | |||
147 | 148 | ||
148 | add_keyword( | 149 | add_keyword( |
149 | "return", | 150 | "return", |
150 | match (ctx.can_be_stmt, fn_def.ret_type().is_some()) { | 151 | match (ctx.can_be_stmt(), fn_def.ret_type().is_some()) { |
151 | (true, true) => "return $0;", | 152 | (true, true) => "return $0;", |
152 | (true, false) => "return;", | 153 | (true, false) => "return;", |
153 | (false, true) => "return $0", | 154 | (false, true) => "return $0", |
diff --git a/crates/ide_completion/src/completions/postfix.rs b/crates/ide_completion/src/completions/postfix.rs index 86bbb58e2..86eb21714 100644 --- a/crates/ide_completion/src/completions/postfix.rs +++ b/crates/ide_completion/src/completions/postfix.rs | |||
@@ -24,7 +24,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
24 | } | 24 | } |
25 | 25 | ||
26 | let (dot_receiver, receiver_is_ambiguous_float_literal) = match &ctx.completion_location { | 26 | let (dot_receiver, receiver_is_ambiguous_float_literal) = match &ctx.completion_location { |
27 | Some(ImmediateLocation::MethodCall { receiver: Some(it) }) => (it, false), | 27 | Some(ImmediateLocation::MethodCall { receiver: Some(it), .. }) => (it, false), |
28 | Some(ImmediateLocation::FieldAccess { | 28 | Some(ImmediateLocation::FieldAccess { |
29 | receiver: Some(it), | 29 | receiver: Some(it), |
30 | receiver_is_ambiguous_float_literal, | 30 | receiver_is_ambiguous_float_literal, |
diff --git a/crates/ide_completion/src/completions/postfix/format_like.rs b/crates/ide_completion/src/completions/postfix/format_like.rs index 0dcb3e898..9ebe1dcc0 100644 --- a/crates/ide_completion/src/completions/postfix/format_like.rs +++ b/crates/ide_completion/src/completions/postfix/format_like.rs | |||
@@ -4,15 +4,15 @@ | |||
4 | // | 4 | // |
5 | // The following postfix snippets are available: | 5 | // The following postfix snippets are available: |
6 | // | 6 | // |
7 | // - `format` -> `format!(...)` | 7 | // * `format` -> `format!(...)` |
8 | // - `panic` -> `panic!(...)` | 8 | // * `panic` -> `panic!(...)` |
9 | // - `println` -> `println!(...)` | 9 | // * `println` -> `println!(...)` |
10 | // - `log`: | 10 | // * `log`: |
11 | // + `logd` -> `log::debug!(...)` | 11 | // ** `logd` -> `log::debug!(...)` |
12 | // + `logt` -> `log::trace!(...)` | 12 | // ** `logt` -> `log::trace!(...)` |
13 | // + `logi` -> `log::info!(...)` | 13 | // ** `logi` -> `log::info!(...)` |
14 | // + `logw` -> `log::warn!(...)` | 14 | // ** `logw` -> `log::warn!(...)` |
15 | // + `loge` -> `log::error!(...)` | 15 | // ** `loge` -> `log::error!(...)` |
16 | // | 16 | // |
17 | // image::https://user-images.githubusercontent.com/48062697/113020656-b560f500-917a-11eb-87de-02991f61beb8.gif[] | 17 | // image::https://user-images.githubusercontent.com/48062697/113020656-b560f500-917a-11eb-87de-02991f61beb8.gif[] |
18 | 18 | ||
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs index de58ce1cd..c072de7b5 100644 --- a/crates/ide_completion/src/completions/qualified_path.rs +++ b/crates/ide_completion/src/completions/qualified_path.rs | |||
@@ -10,8 +10,8 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
10 | if ctx.is_path_disallowed() || ctx.expects_item() { | 10 | if ctx.is_path_disallowed() || ctx.expects_item() { |
11 | return; | 11 | return; |
12 | } | 12 | } |
13 | let path = match &ctx.path_qual { | 13 | let path = match ctx.path_qual() { |
14 | Some(path) => path.clone(), | 14 | Some(path) => path, |
15 | None => return, | 15 | None => return, |
16 | }; | 16 | }; |
17 | 17 | ||
diff --git a/crates/ide_completion/src/completions/snippet.rs b/crates/ide_completion/src/completions/snippet.rs index 6e6a6eb92..59a338e7b 100644 --- a/crates/ide_completion/src/completions/snippet.rs +++ b/crates/ide_completion/src/completions/snippet.rs | |||
@@ -14,7 +14,7 @@ fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) | |||
14 | } | 14 | } |
15 | 15 | ||
16 | pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) { | 16 | pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) { |
17 | if !(ctx.is_trivial_path && ctx.function_def.is_some()) { | 17 | if !(ctx.is_trivial_path() && ctx.function_def.is_some()) { |
18 | return; | 18 | return; |
19 | } | 19 | } |
20 | let cap = match ctx.config.snippet_cap { | 20 | let cap = match ctx.config.snippet_cap { |
@@ -22,7 +22,7 @@ pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionConte | |||
22 | None => return, | 22 | None => return, |
23 | }; | 23 | }; |
24 | 24 | ||
25 | if ctx.can_be_stmt { | 25 | if ctx.can_be_stmt() { |
26 | snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc); | 26 | snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc); |
27 | snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc); | 27 | snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc); |
28 | } | 28 | } |
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs index bd955aa85..f321ed52b 100644 --- a/crates/ide_completion/src/completions/unqualified_path.rs +++ b/crates/ide_completion/src/completions/unqualified_path.rs | |||
@@ -5,7 +5,7 @@ use hir::ScopeDef; | |||
5 | use crate::{CompletionContext, Completions}; | 5 | use crate::{CompletionContext, Completions}; |
6 | 6 | ||
7 | pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { | 7 | pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { |
8 | if !ctx.is_trivial_path { | 8 | if !ctx.is_trivial_path() { |
9 | return; | 9 | return; |
10 | } | 10 | } |
11 | if ctx.is_path_disallowed() || ctx.expects_item() { | 11 | if ctx.is_path_disallowed() || ctx.expects_item() { |
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index cb4f08e53..20e033d31 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs | |||
@@ -29,6 +29,28 @@ pub(crate) enum PatternRefutability { | |||
29 | Irrefutable, | 29 | Irrefutable, |
30 | } | 30 | } |
31 | 31 | ||
32 | #[derive(Debug)] | ||
33 | pub(crate) struct PathCompletionContext { | ||
34 | /// If this is a call with () already there | ||
35 | call_kind: Option<CallKind>, | ||
36 | /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. | ||
37 | pub(super) is_trivial_path: bool, | ||
38 | /// If not a trivial path, the prefix (qualifier). | ||
39 | pub(super) path_qual: Option<ast::Path>, | ||
40 | pub(super) is_path_type: bool, | ||
41 | pub(super) has_type_args: bool, | ||
42 | /// `true` if we are a statement or a last expr in the block. | ||
43 | pub(super) can_be_stmt: bool, | ||
44 | /// `true` if we expect an expression at the cursor position. | ||
45 | pub(super) is_expr: bool, | ||
46 | } | ||
47 | |||
48 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
49 | pub(crate) enum CallKind { | ||
50 | Pat, | ||
51 | Mac, | ||
52 | Expr, | ||
53 | } | ||
32 | /// `CompletionContext` is created early during completion to figure out, where | 54 | /// `CompletionContext` is created early during completion to figure out, where |
33 | /// exactly is the cursor, syntax-wise. | 55 | /// exactly is the cursor, syntax-wise. |
34 | #[derive(Debug)] | 56 | #[derive(Debug)] |
@@ -68,24 +90,9 @@ pub(crate) struct CompletionContext<'a> { | |||
68 | pub(super) prev_sibling: Option<ImmediatePrevSibling>, | 90 | pub(super) prev_sibling: Option<ImmediatePrevSibling>, |
69 | pub(super) attribute_under_caret: Option<ast::Attr>, | 91 | pub(super) attribute_under_caret: Option<ast::Attr>, |
70 | 92 | ||
93 | pub(super) path_context: Option<PathCompletionContext>, | ||
71 | /// FIXME: `ActiveParameter` is string-based, which is very very wrong | 94 | /// FIXME: `ActiveParameter` is string-based, which is very very wrong |
72 | pub(super) active_parameter: Option<ActiveParameter>, | 95 | pub(super) active_parameter: Option<ActiveParameter>, |
73 | /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. | ||
74 | pub(super) is_trivial_path: bool, | ||
75 | /// If not a trivial path, the prefix (qualifier). | ||
76 | pub(super) path_qual: Option<ast::Path>, | ||
77 | /// `true` if we are a statement or a last expr in the block. | ||
78 | pub(super) can_be_stmt: bool, | ||
79 | /// `true` if we expect an expression at the cursor position. | ||
80 | pub(super) is_expr: bool, | ||
81 | /// If this is a call (method or function) in particular, i.e. the () are already there. | ||
82 | pub(super) is_call: bool, | ||
83 | /// Like `is_call`, but for tuple patterns. | ||
84 | pub(super) is_pattern_call: bool, | ||
85 | /// If this is a macro call, i.e. the () are already there. | ||
86 | pub(super) is_macro_call: bool, | ||
87 | pub(super) is_path_type: bool, | ||
88 | pub(super) has_type_args: bool, | ||
89 | pub(super) locals: Vec<(String, Local)>, | 96 | pub(super) locals: Vec<(String, Local)>, |
90 | 97 | ||
91 | pub(super) previous_token: Option<SyntaxToken>, | 98 | pub(super) previous_token: Option<SyntaxToken>, |
@@ -149,15 +156,7 @@ impl<'a> CompletionContext<'a> { | |||
149 | is_label_ref: false, | 156 | is_label_ref: false, |
150 | is_param: false, | 157 | is_param: false, |
151 | is_pat_or_const: None, | 158 | is_pat_or_const: None, |
152 | is_trivial_path: false, | 159 | path_context: None, |
153 | path_qual: None, | ||
154 | can_be_stmt: false, | ||
155 | is_expr: false, | ||
156 | is_call: false, | ||
157 | is_pattern_call: false, | ||
158 | is_macro_call: false, | ||
159 | is_path_type: false, | ||
160 | has_type_args: false, | ||
161 | previous_token: None, | 160 | previous_token: None, |
162 | in_loop_body: false, | 161 | in_loop_body: false, |
163 | completion_location: None, | 162 | completion_location: None, |
@@ -250,14 +249,14 @@ impl<'a> CompletionContext<'a> { | |||
250 | pub(crate) fn has_dot_receiver(&self) -> bool { | 249 | pub(crate) fn has_dot_receiver(&self) -> bool { |
251 | matches!( | 250 | matches!( |
252 | &self.completion_location, | 251 | &self.completion_location, |
253 | Some(ImmediateLocation::FieldAccess { receiver, .. }) | Some(ImmediateLocation::MethodCall { receiver }) | 252 | Some(ImmediateLocation::FieldAccess { receiver, .. }) | Some(ImmediateLocation::MethodCall { receiver,.. }) |
254 | if receiver.is_some() | 253 | if receiver.is_some() |
255 | ) | 254 | ) |
256 | } | 255 | } |
257 | 256 | ||
258 | pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> { | 257 | pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> { |
259 | match &self.completion_location { | 258 | match &self.completion_location { |
260 | Some(ImmediateLocation::MethodCall { receiver }) | 259 | Some(ImmediateLocation::MethodCall { receiver, .. }) |
261 | | Some(ImmediateLocation::FieldAccess { receiver, .. }) => receiver.as_ref(), | 260 | | Some(ImmediateLocation::FieldAccess { receiver, .. }) => receiver.as_ref(), |
262 | _ => None, | 261 | _ => None, |
263 | } | 262 | } |
@@ -275,11 +274,6 @@ impl<'a> CompletionContext<'a> { | |||
275 | matches!(self.completion_location, Some(ImmediateLocation::ItemList)) | 274 | matches!(self.completion_location, Some(ImmediateLocation::ItemList)) |
276 | } | 275 | } |
277 | 276 | ||
278 | // fn expects_value(&self) -> bool { | ||
279 | pub(crate) fn expects_expression(&self) -> bool { | ||
280 | self.is_expr | ||
281 | } | ||
282 | |||
283 | pub(crate) fn has_block_expr_parent(&self) -> bool { | 277 | pub(crate) fn has_block_expr_parent(&self) -> bool { |
284 | matches!(self.completion_location, Some(ImmediateLocation::BlockExpr)) | 278 | matches!(self.completion_location, Some(ImmediateLocation::BlockExpr)) |
285 | } | 279 | } |
@@ -316,6 +310,26 @@ impl<'a> CompletionContext<'a> { | |||
316 | ) || self.attribute_under_caret.is_some() | 310 | ) || self.attribute_under_caret.is_some() |
317 | } | 311 | } |
318 | 312 | ||
313 | pub(crate) fn expects_expression(&self) -> bool { | ||
314 | self.path_context.as_ref().map_or(false, |it| it.is_expr) | ||
315 | } | ||
316 | |||
317 | pub(crate) fn path_call_kind(&self) -> Option<CallKind> { | ||
318 | self.path_context.as_ref().and_then(|it| it.call_kind) | ||
319 | } | ||
320 | |||
321 | pub(crate) fn is_trivial_path(&self) -> bool { | ||
322 | self.path_context.as_ref().map_or(false, |it| it.is_trivial_path) | ||
323 | } | ||
324 | |||
325 | pub(crate) fn path_qual(&self) -> Option<&ast::Path> { | ||
326 | self.path_context.as_ref().and_then(|it| it.path_qual.as_ref()) | ||
327 | } | ||
328 | |||
329 | pub(crate) fn can_be_stmt(&self) -> bool { | ||
330 | self.path_context.as_ref().map_or(false, |it| it.can_be_stmt) | ||
331 | } | ||
332 | |||
319 | fn fill_impl_def(&mut self) { | 333 | fn fill_impl_def(&mut self) { |
320 | self.impl_def = self | 334 | self.impl_def = self |
321 | .sema | 335 | .sema |
@@ -568,22 +582,32 @@ impl<'a> CompletionContext<'a> { | |||
568 | }; | 582 | }; |
569 | 583 | ||
570 | if let Some(segment) = ast::PathSegment::cast(parent) { | 584 | if let Some(segment) = ast::PathSegment::cast(parent) { |
585 | let path_ctx = self.path_context.get_or_insert(PathCompletionContext { | ||
586 | call_kind: None, | ||
587 | is_trivial_path: false, | ||
588 | path_qual: None, | ||
589 | has_type_args: false, | ||
590 | is_path_type: false, | ||
591 | can_be_stmt: false, | ||
592 | is_expr: false, | ||
593 | }); | ||
571 | let path = segment.parent_path(); | 594 | let path = segment.parent_path(); |
572 | self.is_call = path | ||
573 | .syntax() | ||
574 | .parent() | ||
575 | .and_then(ast::PathExpr::cast) | ||
576 | .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast)) | ||
577 | .is_some(); | ||
578 | self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some(); | ||
579 | self.is_pattern_call = | ||
580 | path.syntax().parent().and_then(ast::TupleStructPat::cast).is_some(); | ||
581 | 595 | ||
582 | self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); | 596 | if let Some(p) = path.syntax().parent() { |
583 | self.has_type_args = segment.generic_arg_list().is_some(); | 597 | path_ctx.call_kind = match_ast! { |
598 | match p { | ||
599 | ast::PathExpr(it) => it.syntax().parent().and_then(ast::CallExpr::cast).map(|_| CallKind::Expr), | ||
600 | ast::MacroCall(_it) => Some(CallKind::Mac), | ||
601 | ast::TupleStructPat(_it) => Some(CallKind::Pat), | ||
602 | _ => None | ||
603 | } | ||
604 | }; | ||
605 | } | ||
606 | path_ctx.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); | ||
607 | path_ctx.has_type_args = segment.generic_arg_list().is_some(); | ||
584 | 608 | ||
585 | if let Some(path) = path_or_use_tree_qualifier(&path) { | 609 | if let Some(path) = path_or_use_tree_qualifier(&path) { |
586 | self.path_qual = path | 610 | path_ctx.path_qual = path |
587 | .segment() | 611 | .segment() |
588 | .and_then(|it| { | 612 | .and_then(|it| { |
589 | find_node_with_range::<ast::PathSegment>( | 613 | find_node_with_range::<ast::PathSegment>( |
@@ -601,11 +625,11 @@ impl<'a> CompletionContext<'a> { | |||
601 | } | 625 | } |
602 | } | 626 | } |
603 | 627 | ||
604 | self.is_trivial_path = true; | 628 | path_ctx.is_trivial_path = true; |
605 | 629 | ||
606 | // Find either enclosing expr statement (thing with `;`) or a | 630 | // Find either enclosing expr statement (thing with `;`) or a |
607 | // block. If block, check that we are the last expr. | 631 | // block. If block, check that we are the last expr. |
608 | self.can_be_stmt = name_ref | 632 | path_ctx.can_be_stmt = name_ref |
609 | .syntax() | 633 | .syntax() |
610 | .ancestors() | 634 | .ancestors() |
611 | .find_map(|node| { | 635 | .find_map(|node| { |
@@ -621,10 +645,8 @@ impl<'a> CompletionContext<'a> { | |||
621 | None | 645 | None |
622 | }) | 646 | }) |
623 | .unwrap_or(false); | 647 | .unwrap_or(false); |
624 | self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some(); | 648 | path_ctx.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some(); |
625 | } | 649 | } |
626 | self.is_call |= | ||
627 | matches!(self.completion_location, Some(ImmediateLocation::MethodCall { .. })); | ||
628 | } | 650 | } |
629 | } | 651 | } |
630 | 652 | ||
diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs index 080898aef..251d76fe9 100644 --- a/crates/ide_completion/src/patterns.rs +++ b/crates/ide_completion/src/patterns.rs | |||
@@ -4,7 +4,7 @@ use hir::Semantics; | |||
4 | use ide_db::RootDatabase; | 4 | use ide_db::RootDatabase; |
5 | use syntax::{ | 5 | use syntax::{ |
6 | algo::non_trivia_sibling, | 6 | algo::non_trivia_sibling, |
7 | ast::{self, LoopBodyOwner}, | 7 | ast::{self, ArgListOwner, LoopBodyOwner}, |
8 | match_ast, AstNode, Direction, SyntaxElement, | 8 | match_ast, AstNode, Direction, SyntaxElement, |
9 | SyntaxKind::*, | 9 | SyntaxKind::*, |
10 | SyntaxNode, SyntaxToken, TextRange, TextSize, T, | 10 | SyntaxNode, SyntaxToken, TextRange, TextSize, T, |
@@ -39,6 +39,7 @@ pub(crate) enum ImmediateLocation { | |||
39 | // Original file ast node | 39 | // Original file ast node |
40 | MethodCall { | 40 | MethodCall { |
41 | receiver: Option<ast::Expr>, | 41 | receiver: Option<ast::Expr>, |
42 | has_parens: bool, | ||
42 | }, | 43 | }, |
43 | // Original file ast node | 44 | // Original file ast node |
44 | FieldAccess { | 45 | FieldAccess { |
@@ -204,6 +205,7 @@ pub(crate) fn determine_location( | |||
204 | .receiver() | 205 | .receiver() |
205 | .map(|e| e.syntax().text_range()) | 206 | .map(|e| e.syntax().text_range()) |
206 | .and_then(|r| find_node_with_range(original_file, r)), | 207 | .and_then(|r| find_node_with_range(original_file, r)), |
208 | has_parens: it.arg_list().map_or(false, |it| it.l_paren_token().is_some()) | ||
207 | }, | 209 | }, |
208 | _ => return None, | 210 | _ => return None, |
209 | } | 211 | } |
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index a49a60711..750694432 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs | |||
@@ -275,8 +275,12 @@ impl<'a> Render<'a> { | |||
275 | }; | 275 | }; |
276 | 276 | ||
277 | // Add `<>` for generic types | 277 | // Add `<>` for generic types |
278 | if self.ctx.completion.is_path_type | 278 | if self |
279 | && !self.ctx.completion.has_type_args | 279 | .ctx |
280 | .completion | ||
281 | .path_context | ||
282 | .as_ref() | ||
283 | .map_or(false, |it| it.is_path_type && !it.has_type_args) | ||
280 | && self.ctx.completion.config.add_call_parenthesis | 284 | && self.ctx.completion.config.add_call_parenthesis |
281 | { | 285 | { |
282 | if let Some(cap) = self.ctx.snippet_cap() { | 286 | if let Some(cap) = self.ctx.snippet_cap() { |
diff --git a/crates/ide_completion/src/render/builder_ext.rs b/crates/ide_completion/src/render/builder_ext.rs index 6d062b3b9..c54752d30 100644 --- a/crates/ide_completion/src/render/builder_ext.rs +++ b/crates/ide_completion/src/render/builder_ext.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | use itertools::Itertools; | 3 | use itertools::Itertools; |
4 | 4 | ||
5 | use crate::{item::Builder, CompletionContext}; | 5 | use crate::{context::CallKind, item::Builder, patterns::ImmediateLocation, CompletionContext}; |
6 | 6 | ||
7 | #[derive(Debug)] | 7 | #[derive(Debug)] |
8 | pub(super) enum Params { | 8 | pub(super) enum Params { |
@@ -32,10 +32,12 @@ impl Builder { | |||
32 | cov_mark::hit!(no_parens_in_use_item); | 32 | cov_mark::hit!(no_parens_in_use_item); |
33 | return false; | 33 | return false; |
34 | } | 34 | } |
35 | if ctx.is_pattern_call { | 35 | if matches!(ctx.path_call_kind(), Some(CallKind::Expr) | Some(CallKind::Pat)) |
36 | return false; | 36 | | matches!( |
37 | } | 37 | ctx.completion_location, |
38 | if ctx.is_call { | 38 | Some(ImmediateLocation::MethodCall { has_parens: true, .. }) |
39 | ) | ||
40 | { | ||
39 | return false; | 41 | return false; |
40 | } | 42 | } |
41 | 43 | ||
diff --git a/crates/ide_completion/src/render/macro_.rs b/crates/ide_completion/src/render/macro_.rs index 0dfba8acc..429d937c8 100644 --- a/crates/ide_completion/src/render/macro_.rs +++ b/crates/ide_completion/src/render/macro_.rs | |||
@@ -5,6 +5,7 @@ use ide_db::SymbolKind; | |||
5 | use syntax::display::macro_label; | 5 | use syntax::display::macro_label; |
6 | 6 | ||
7 | use crate::{ | 7 | use crate::{ |
8 | context::CallKind, | ||
8 | item::{CompletionItem, CompletionKind, ImportEdit}, | 9 | item::{CompletionItem, CompletionKind, ImportEdit}, |
9 | render::RenderContext, | 10 | render::RenderContext, |
10 | }; | 11 | }; |
@@ -68,7 +69,8 @@ impl<'a> MacroRender<'a> { | |||
68 | } | 69 | } |
69 | 70 | ||
70 | fn needs_bang(&self) -> bool { | 71 | fn needs_bang(&self) -> bool { |
71 | self.ctx.completion.use_item_syntax.is_none() && !self.ctx.completion.is_macro_call | 72 | self.ctx.completion.use_item_syntax.is_none() |
73 | && !matches!(self.ctx.completion.path_call_kind(), Some(CallKind::Mac)) | ||
72 | } | 74 | } |
73 | 75 | ||
74 | fn label(&self) -> String { | 76 | fn label(&self) -> String { |