diff options
-rw-r--r-- | crates/hir_def/src/attr.rs | 4 | ||||
-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 |
4 files changed, 75 insertions, 40 deletions
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs index 385ba8c80..b7e72b790 100644 --- a/crates/hir_def/src/attr.rs +++ b/crates/hir_def/src/attr.rs | |||
@@ -36,6 +36,10 @@ use crate::{ | |||
36 | pub struct Documentation(String); | 36 | pub struct Documentation(String); |
37 | 37 | ||
38 | impl Documentation { | 38 | impl Documentation { |
39 | pub fn new(s: impl Into<String>) -> Self { | ||
40 | Documentation(s.into()) | ||
41 | } | ||
42 | |||
39 | pub fn as_str(&self) -> &str { | 43 | pub fn as_str(&self) -> &str { |
40 | &self.0 | 44 | &self.0 |
41 | } | 45 | } |
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 | } |