Skip to content

Commit 4cb10bb

Browse files
committed
Adds attribute completions (#3941)
1 parent 278bf35 commit 4cb10bb

File tree

5 files changed

+117
-0
lines changed

5 files changed

+117
-0
lines changed

crates/ra_ide/src/completion.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ mod completion_item;
44
mod completion_context;
55
mod presentation;
66

7+
mod complete_attribute;
78
mod complete_dot;
89
mod complete_record;
910
mod complete_pattern;
@@ -93,6 +94,7 @@ pub(crate) fn completions(
9394
complete_postfix::complete_postfix(&mut acc, &ctx);
9495
complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx);
9596
complete_trait_impl::complete_trait_impl(&mut acc, &ctx);
97+
complete_attribute::complete_attribute(&mut acc, &ctx);
9698

9799
Some(acc)
98100
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
//! Completion for attributes
2+
//!
3+
//! This module uses a bit of static metadata to provide completions
4+
//! for built-in attributes.
5+
6+
use super::completion_context::CompletionContext;
7+
use super::completion_item::{CompletionItem, CompletionItemKind, CompletionKind, Completions};
8+
use ra_syntax::{ast, AstNode};
9+
10+
const ATTRIBUTES: &[AttrCompletion] = &[
11+
AttrCompletion { label: "allow", snippet: Some("allow(${0:lint})"), should_be_inner: false },
12+
AttrCompletion {
13+
label: "cfg_attr",
14+
snippet: Some("cfg_attr(${1:flag}, ${0:attr})"),
15+
should_be_inner: false,
16+
},
17+
AttrCompletion { label: "cfg", snippet: Some("cfg(${0:flag})"), should_be_inner: false },
18+
AttrCompletion { label: "deny", snippet: Some("deny(${0:lint})"), should_be_inner: false },
19+
AttrCompletion {
20+
label: "deprecated",
21+
snippet: Some(r#"deprecated = "${0:warning}""#),
22+
should_be_inner: false,
23+
},
24+
AttrCompletion {
25+
label: "derive",
26+
snippet: Some(r#"derive(${0:Debug})"#),
27+
should_be_inner: false,
28+
},
29+
AttrCompletion { label: "doc", snippet: Some(r#"doc = "${0:docs}""#), should_be_inner: false },
30+
AttrCompletion { label: "feature", snippet: Some("feature(${0:flag})"), should_be_inner: true },
31+
AttrCompletion { label: "global_allocator", snippet: None, should_be_inner: true },
32+
AttrCompletion { label: "ignore", snippet: Some("ignore(${0:lint})"), should_be_inner: false },
33+
AttrCompletion { label: "inline", snippet: Some("inline(${0:lint})"), should_be_inner: false },
34+
AttrCompletion {
35+
label: "link_name",
36+
snippet: Some(r#"link_name = "${0:symbol_name}""#),
37+
should_be_inner: false,
38+
},
39+
AttrCompletion { label: "link", snippet: None, should_be_inner: false },
40+
AttrCompletion { label: "macro_export", snippet: None, should_be_inner: false },
41+
AttrCompletion { label: "macro_use", snippet: None, should_be_inner: false },
42+
AttrCompletion {
43+
label: "must_use",
44+
snippet: Some(r#"must_use = "${0:message}""#),
45+
should_be_inner: false,
46+
},
47+
AttrCompletion { label: "no_mangle", snippet: None, should_be_inner: false },
48+
AttrCompletion { label: "no_std", snippet: None, should_be_inner: true },
49+
AttrCompletion { label: "non_exhaustive", snippet: None, should_be_inner: false },
50+
AttrCompletion { label: "panic_handler", snippet: None, should_be_inner: true },
51+
AttrCompletion { label: "path", snippet: Some("path =\"${0:path}\""), should_be_inner: false },
52+
AttrCompletion {
53+
label: "recursion_limit",
54+
snippet: Some("recursion_limit = ${0:128}"),
55+
should_be_inner: true,
56+
},
57+
AttrCompletion { label: "repr", snippet: Some("repr(${0:Rust})"), should_be_inner: false },
58+
AttrCompletion {
59+
label: "should_panic",
60+
snippet: Some(r#"expected = "${0:message}""#),
61+
should_be_inner: false,
62+
},
63+
AttrCompletion {
64+
label: "target_feature",
65+
snippet: Some("target_feature = \"${0:feature}\""),
66+
should_be_inner: false,
67+
},
68+
AttrCompletion { label: "test", snippet: None, should_be_inner: false },
69+
AttrCompletion { label: "used", snippet: None, should_be_inner: false },
70+
AttrCompletion { label: "warn", snippet: Some("warn(${0:lint})"), should_be_inner: false },
71+
AttrCompletion {
72+
label: "windows_subsystem",
73+
snippet: Some(r#"windows_subsystem = "${0:subsystem}""#),
74+
should_be_inner: true,
75+
},
76+
];
77+
78+
struct AttrCompletion {
79+
label: &'static str,
80+
snippet: Option<&'static str>,
81+
should_be_inner: bool,
82+
}
83+
84+
pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) {
85+
if !ctx.is_attribute {
86+
return;
87+
}
88+
89+
let is_inner = ctx
90+
.name_ref_syntax
91+
.as_ref()
92+
.into_iter()
93+
.flat_map(|name_ref| name_ref.syntax().ancestors())
94+
.filter_map(ast::Attr::cast)
95+
.next()
96+
.and_then(|attr| attr.excl_token())
97+
.is_some();
98+
99+
for attr_completion in ATTRIBUTES {
100+
let mut item =
101+
CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), attr_completion.label)
102+
.kind(CompletionItemKind::Attribute);
103+
if let Some(snippet) = attr_completion.snippet {
104+
item = item.insert_snippet(snippet);
105+
}
106+
if is_inner || !attr_completion.should_be_inner {
107+
acc.add(item);
108+
}
109+
}
110+
}

crates/ra_ide/src/completion/completion_context.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ pub(crate) struct CompletionContext<'a> {
5454
pub(super) is_macro_call: bool,
5555
pub(super) is_path_type: bool,
5656
pub(super) has_type_args: bool,
57+
pub(super) is_attribute: bool,
5758
}
5859

5960
impl<'a> CompletionContext<'a> {
@@ -108,6 +109,7 @@ impl<'a> CompletionContext<'a> {
108109
is_path_type: false,
109110
has_type_args: false,
110111
dot_receiver_is_ambiguous_float_literal: false,
112+
is_attribute: false,
111113
};
112114

113115
let mut original_file = original_file.syntax().clone();
@@ -293,6 +295,7 @@ impl<'a> CompletionContext<'a> {
293295
.and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast))
294296
.is_some();
295297
self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some();
298+
self.is_attribute = path.syntax().parent().and_then(ast::Attr::cast).is_some();
296299

297300
self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some();
298301
self.has_type_args = segment.type_arg_list().is_some();

crates/ra_ide/src/completion/completion_item.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ pub enum CompletionItemKind {
106106
Method,
107107
TypeParam,
108108
Macro,
109+
Attribute,
109110
}
110111

111112
#[derive(Debug, PartialEq, Eq, Copy, Clone)]

crates/rust-analyzer/src/conv.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ impl Conv for CompletionItemKind {
100100
CompletionItemKind::Method => Method,
101101
CompletionItemKind::TypeParam => TypeParameter,
102102
CompletionItemKind::Macro => Method,
103+
CompletionItemKind::Attribute => EnumMember,
103104
}
104105
}
105106
}

0 commit comments

Comments
 (0)