From 0dab5d58790d46e28d738c0d1d96e833a61495a1 Mon Sep 17 00:00:00 2001 From: Roberto Vidal Date: Thu, 23 Apr 2020 18:22:33 +0200 Subject: Adds attribute completions (#3941) --- crates/ra_ide/src/completion.rs | 2 + crates/ra_ide/src/completion/complete_attribute.rs | 587 +++++++++++++++++++++ crates/ra_ide/src/completion/completion_context.rs | 3 + crates/ra_ide/src/completion/completion_item.rs | 2 + crates/rust-analyzer/src/conv.rs | 1 + 5 files changed, 595 insertions(+) create mode 100644 crates/ra_ide/src/completion/complete_attribute.rs diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs index f0e02180b..4ca0fdf4f 100644 --- a/crates/ra_ide/src/completion.rs +++ b/crates/ra_ide/src/completion.rs @@ -5,6 +5,7 @@ mod completion_item; mod completion_context; mod presentation; +mod complete_attribute; mod complete_dot; mod complete_record; mod complete_pattern; @@ -78,6 +79,7 @@ pub(crate) fn completions( complete_postfix::complete_postfix(&mut acc, &ctx); complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx); complete_trait_impl::complete_trait_impl(&mut acc, &ctx); + complete_attribute::complete_attribute(&mut acc, &ctx); Some(acc) } diff --git a/crates/ra_ide/src/completion/complete_attribute.rs b/crates/ra_ide/src/completion/complete_attribute.rs new file mode 100644 index 000000000..b405042e8 --- /dev/null +++ b/crates/ra_ide/src/completion/complete_attribute.rs @@ -0,0 +1,587 @@ +//! Completion for attributes +//! +//! This module uses a bit of static metadata to provide completions +//! for built-in attributes. + +use super::completion_context::CompletionContext; +use super::completion_item::{CompletionItem, CompletionItemKind, CompletionKind, Completions}; +use ra_syntax::{ + ast::{Attr, AttrKind}, + AstNode, +}; + +pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) { + if !ctx.is_attribute { + return; + } + + let is_inner = ctx + .original_token + .ancestors() + .find_map(Attr::cast) + .map(|attr| attr.kind() == AttrKind::Inner) + .unwrap_or(false); + + for attr_completion in ATTRIBUTES { + let mut item = CompletionItem::new( + CompletionKind::Attribute, + ctx.source_range(), + attr_completion.label, + ) + .kind(CompletionItemKind::Attribute); + + match (attr_completion.snippet, ctx.config.snippet_cap) { + (Some(snippet), Some(cap)) => { + item = item.insert_snippet(cap, snippet); + } + _ => {} + } + + if is_inner || !attr_completion.should_be_inner { + acc.add(item); + } + } +} + +struct AttrCompletion { + label: &'static str, + snippet: Option<&'static str>, + should_be_inner: bool, +} + +const ATTRIBUTES: &[AttrCompletion] = &[ + AttrCompletion { label: "allow", snippet: Some("allow(${0:lint})"), should_be_inner: false }, + AttrCompletion { + label: "cfg_attr", + snippet: Some("cfg_attr(${1:predicate}, ${0:attr})"), + should_be_inner: false, + }, + AttrCompletion { label: "cfg", snippet: Some("cfg(${0:predicate})"), should_be_inner: false }, + AttrCompletion { label: "deny", snippet: Some("deny(${0:lint})"), should_be_inner: false }, + AttrCompletion { + label: "deprecated", + snippet: Some(r#"deprecated = "${0:reason}""#), + should_be_inner: false, + }, + AttrCompletion { + label: "derive", + snippet: Some(r#"derive(${0:Debug})"#), + should_be_inner: false, + }, + AttrCompletion { label: "doc", snippet: Some(r#"doc = "${0:docs}""#), should_be_inner: false }, + AttrCompletion { label: "feature", snippet: Some("feature(${0:flag})"), should_be_inner: true }, + AttrCompletion { label: "forbid", snippet: Some("forbid(${0:lint})"), should_be_inner: false }, + // FIXME: resolve through macro resolution? + AttrCompletion { label: "global_allocator", snippet: None, should_be_inner: true }, + AttrCompletion { label: "ignore", snippet: Some("ignore(${0:lint})"), should_be_inner: false }, + AttrCompletion { label: "inline", snippet: Some("inline(${0:lint})"), should_be_inner: false }, + AttrCompletion { + label: "link_name", + snippet: Some(r#"link_name = "${0:symbol_name}""#), + should_be_inner: false, + }, + AttrCompletion { label: "link", snippet: None, should_be_inner: false }, + AttrCompletion { label: "macro_export", snippet: None, should_be_inner: false }, + AttrCompletion { label: "macro_use", snippet: None, should_be_inner: false }, + AttrCompletion { + label: "must_use", + snippet: Some(r#"must_use = "${0:reason}""#), + should_be_inner: false, + }, + AttrCompletion { label: "no_mangle", snippet: None, should_be_inner: false }, + AttrCompletion { label: "no_std", snippet: None, should_be_inner: true }, + AttrCompletion { label: "non_exhaustive", snippet: None, should_be_inner: false }, + AttrCompletion { label: "panic_handler", snippet: None, should_be_inner: true }, + AttrCompletion { label: "path", snippet: Some("path =\"${0:path}\""), should_be_inner: false }, + AttrCompletion { label: "proc_macro", snippet: None, should_be_inner: false }, + AttrCompletion { label: "proc_macro_attribute", snippet: None, should_be_inner: false }, + AttrCompletion { + label: "proc_macro_derive", + snippet: Some("proc_macro_derive(${0:Trait})"), + should_be_inner: false, + }, + AttrCompletion { + label: "recursion_limit", + snippet: Some("recursion_limit = ${0:128}"), + should_be_inner: true, + }, + AttrCompletion { label: "repr", snippet: Some("repr(${0:C})"), should_be_inner: false }, + AttrCompletion { + label: "should_panic", + snippet: Some(r#"expected = "${0:reason}""#), + should_be_inner: false, + }, + AttrCompletion { + label: "target_feature", + snippet: Some("target_feature = \"${0:feature}\""), + should_be_inner: false, + }, + AttrCompletion { label: "test", snippet: None, should_be_inner: false }, + AttrCompletion { label: "used", snippet: None, should_be_inner: false }, + AttrCompletion { label: "warn", snippet: Some("warn(${0:lint})"), should_be_inner: false }, + AttrCompletion { + label: "windows_subsystem", + snippet: Some(r#"windows_subsystem = "${0:subsystem}""#), + should_be_inner: true, + }, +]; + +#[cfg(test)] +mod tests { + use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; + use insta::assert_debug_snapshot; + + fn do_attr_completion(code: &str) -> Vec { + do_completion(code, CompletionKind::Attribute) + } + + #[test] + fn test_attribute_completion() { + assert_debug_snapshot!( + do_attr_completion( + r" + #[<|>] + ", + ), + @r###" + [ + CompletionItem { + label: "allow", + source_range: [19; 19), + delete: [19; 19), + insert: "allow(${0:lint})", + kind: Attribute, + }, + CompletionItem { + label: "cfg", + source_range: [19; 19), + delete: [19; 19), + insert: "cfg(${0:predicate})", + kind: Attribute, + }, + CompletionItem { + label: "cfg_attr", + source_range: [19; 19), + delete: [19; 19), + insert: "cfg_attr(${1:predicate}, ${0:attr})", + kind: Attribute, + }, + CompletionItem { + label: "deny", + source_range: [19; 19), + delete: [19; 19), + insert: "deny(${0:lint})", + kind: Attribute, + }, + CompletionItem { + label: "deprecated", + source_range: [19; 19), + delete: [19; 19), + insert: "deprecated = \"${0:reason}\"", + kind: Attribute, + }, + CompletionItem { + label: "derive", + source_range: [19; 19), + delete: [19; 19), + insert: "derive(${0:Debug})", + kind: Attribute, + }, + CompletionItem { + label: "doc", + source_range: [19; 19), + delete: [19; 19), + insert: "doc = \"${0:docs}\"", + kind: Attribute, + }, + CompletionItem { + label: "forbid", + source_range: [19; 19), + delete: [19; 19), + insert: "forbid(${0:lint})", + kind: Attribute, + }, + CompletionItem { + label: "ignore", + source_range: [19; 19), + delete: [19; 19), + insert: "ignore(${0:lint})", + kind: Attribute, + }, + CompletionItem { + label: "inline", + source_range: [19; 19), + delete: [19; 19), + insert: "inline(${0:lint})", + kind: Attribute, + }, + CompletionItem { + label: "link", + source_range: [19; 19), + delete: [19; 19), + insert: "link", + kind: Attribute, + }, + CompletionItem { + label: "link_name", + source_range: [19; 19), + delete: [19; 19), + insert: "link_name = \"${0:symbol_name}\"", + kind: Attribute, + }, + CompletionItem { + label: "macro_export", + source_range: [19; 19), + delete: [19; 19), + insert: "macro_export", + kind: Attribute, + }, + CompletionItem { + label: "macro_use", + source_range: [19; 19), + delete: [19; 19), + insert: "macro_use", + kind: Attribute, + }, + CompletionItem { + label: "must_use", + source_range: [19; 19), + delete: [19; 19), + insert: "must_use = \"${0:reason}\"", + kind: Attribute, + }, + CompletionItem { + label: "no_mangle", + source_range: [19; 19), + delete: [19; 19), + insert: "no_mangle", + kind: Attribute, + }, + CompletionItem { + label: "non_exhaustive", + source_range: [19; 19), + delete: [19; 19), + insert: "non_exhaustive", + kind: Attribute, + }, + CompletionItem { + label: "path", + source_range: [19; 19), + delete: [19; 19), + insert: "path =\"${0:path}\"", + kind: Attribute, + }, + CompletionItem { + label: "proc_macro", + source_range: [19; 19), + delete: [19; 19), + insert: "proc_macro", + kind: Attribute, + }, + CompletionItem { + label: "proc_macro_attribute", + source_range: [19; 19), + delete: [19; 19), + insert: "proc_macro_attribute", + kind: Attribute, + }, + CompletionItem { + label: "proc_macro_derive", + source_range: [19; 19), + delete: [19; 19), + insert: "proc_macro_derive(${0:Trait})", + kind: Attribute, + }, + CompletionItem { + label: "repr", + source_range: [19; 19), + delete: [19; 19), + insert: "repr(${0:C})", + kind: Attribute, + }, + CompletionItem { + label: "should_panic", + source_range: [19; 19), + delete: [19; 19), + insert: "expected = \"${0:reason}\"", + kind: Attribute, + }, + CompletionItem { + label: "target_feature", + source_range: [19; 19), + delete: [19; 19), + insert: "target_feature = \"${0:feature}\"", + kind: Attribute, + }, + CompletionItem { + label: "test", + source_range: [19; 19), + delete: [19; 19), + insert: "test", + kind: Attribute, + }, + CompletionItem { + label: "used", + source_range: [19; 19), + delete: [19; 19), + insert: "used", + kind: Attribute, + }, + CompletionItem { + label: "warn", + source_range: [19; 19), + delete: [19; 19), + insert: "warn(${0:lint})", + kind: Attribute, + }, + ] + "### + ); + } + + #[test] + fn test_inner_attribute_completion() { + assert_debug_snapshot!( + do_attr_completion( + r" + #![<|>] + ", + ), + @r###" + [ + CompletionItem { + label: "allow", + source_range: [20; 20), + delete: [20; 20), + insert: "allow(${0:lint})", + kind: Attribute, + }, + CompletionItem { + label: "cfg", + source_range: [20; 20), + delete: [20; 20), + insert: "cfg(${0:predicate})", + kind: Attribute, + }, + CompletionItem { + label: "cfg_attr", + source_range: [20; 20), + delete: [20; 20), + insert: "cfg_attr(${1:predicate}, ${0:attr})", + kind: Attribute, + }, + CompletionItem { + label: "deny", + source_range: [20; 20), + delete: [20; 20), + insert: "deny(${0:lint})", + kind: Attribute, + }, + CompletionItem { + label: "deprecated", + source_range: [20; 20), + delete: [20; 20), + insert: "deprecated = \"${0:reason}\"", + kind: Attribute, + }, + CompletionItem { + label: "derive", + source_range: [20; 20), + delete: [20; 20), + insert: "derive(${0:Debug})", + kind: Attribute, + }, + CompletionItem { + label: "doc", + source_range: [20; 20), + delete: [20; 20), + insert: "doc = \"${0:docs}\"", + kind: Attribute, + }, + CompletionItem { + label: "feature", + source_range: [20; 20), + delete: [20; 20), + insert: "feature(${0:flag})", + kind: Attribute, + }, + CompletionItem { + label: "forbid", + source_range: [20; 20), + delete: [20; 20), + insert: "forbid(${0:lint})", + kind: Attribute, + }, + CompletionItem { + label: "global_allocator", + source_range: [20; 20), + delete: [20; 20), + insert: "global_allocator", + kind: Attribute, + }, + CompletionItem { + label: "ignore", + source_range: [20; 20), + delete: [20; 20), + insert: "ignore(${0:lint})", + kind: Attribute, + }, + CompletionItem { + label: "inline", + source_range: [20; 20), + delete: [20; 20), + insert: "inline(${0:lint})", + kind: Attribute, + }, + CompletionItem { + label: "link", + source_range: [20; 20), + delete: [20; 20), + insert: "link", + kind: Attribute, + }, + CompletionItem { + label: "link_name", + source_range: [20; 20), + delete: [20; 20), + insert: "link_name = \"${0:symbol_name}\"", + kind: Attribute, + }, + CompletionItem { + label: "macro_export", + source_range: [20; 20), + delete: [20; 20), + insert: "macro_export", + kind: Attribute, + }, + CompletionItem { + label: "macro_use", + source_range: [20; 20), + delete: [20; 20), + insert: "macro_use", + kind: Attribute, + }, + CompletionItem { + label: "must_use", + source_range: [20; 20), + delete: [20; 20), + insert: "must_use = \"${0:reason}\"", + kind: Attribute, + }, + CompletionItem { + label: "no_mangle", + source_range: [20; 20), + delete: [20; 20), + insert: "no_mangle", + kind: Attribute, + }, + CompletionItem { + label: "no_std", + source_range: [20; 20), + delete: [20; 20), + insert: "no_std", + kind: Attribute, + }, + CompletionItem { + label: "non_exhaustive", + source_range: [20; 20), + delete: [20; 20), + insert: "non_exhaustive", + kind: Attribute, + }, + CompletionItem { + label: "panic_handler", + source_range: [20; 20), + delete: [20; 20), + insert: "panic_handler", + kind: Attribute, + }, + CompletionItem { + label: "path", + source_range: [20; 20), + delete: [20; 20), + insert: "path =\"${0:path}\"", + kind: Attribute, + }, + CompletionItem { + label: "proc_macro", + source_range: [20; 20), + delete: [20; 20), + insert: "proc_macro", + kind: Attribute, + }, + CompletionItem { + label: "proc_macro_attribute", + source_range: [20; 20), + delete: [20; 20), + insert: "proc_macro_attribute", + kind: Attribute, + }, + CompletionItem { + label: "proc_macro_derive", + source_range: [20; 20), + delete: [20; 20), + insert: "proc_macro_derive(${0:Trait})", + kind: Attribute, + }, + CompletionItem { + label: "recursion_limit", + source_range: [20; 20), + delete: [20; 20), + insert: "recursion_limit = ${0:128}", + kind: Attribute, + }, + CompletionItem { + label: "repr", + source_range: [20; 20), + delete: [20; 20), + insert: "repr(${0:C})", + kind: Attribute, + }, + CompletionItem { + label: "should_panic", + source_range: [20; 20), + delete: [20; 20), + insert: "expected = \"${0:reason}\"", + kind: Attribute, + }, + CompletionItem { + label: "target_feature", + source_range: [20; 20), + delete: [20; 20), + insert: "target_feature = \"${0:feature}\"", + kind: Attribute, + }, + CompletionItem { + label: "test", + source_range: [20; 20), + delete: [20; 20), + insert: "test", + kind: Attribute, + }, + CompletionItem { + label: "used", + source_range: [20; 20), + delete: [20; 20), + insert: "used", + kind: Attribute, + }, + CompletionItem { + label: "warn", + source_range: [20; 20), + delete: [20; 20), + insert: "warn(${0:lint})", + kind: Attribute, + }, + CompletionItem { + label: "windows_subsystem", + source_range: [20; 20), + delete: [20; 20), + insert: "windows_subsystem = \"${0:subsystem}\"", + kind: Attribute, + }, + ] + "### + ); + } +} diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index a76d1ce45..37880448a 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs @@ -57,6 +57,7 @@ pub(crate) struct CompletionContext<'a> { pub(super) is_macro_call: bool, pub(super) is_path_type: bool, pub(super) has_type_args: bool, + pub(super) is_attribute: bool, } impl<'a> CompletionContext<'a> { @@ -113,6 +114,7 @@ impl<'a> CompletionContext<'a> { is_path_type: false, has_type_args: false, dot_receiver_is_ambiguous_float_literal: false, + is_attribute: false, }; let mut original_file = original_file.syntax().clone(); @@ -306,6 +308,7 @@ impl<'a> CompletionContext<'a> { .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast)) .is_some(); self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some(); + self.is_attribute = path.syntax().parent().and_then(ast::Attr::cast).is_some(); self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); self.has_type_args = segment.type_arg_list().is_some(); diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs index fb06cc125..5936fb8f7 100644 --- a/crates/ra_ide/src/completion/completion_item.rs +++ b/crates/ra_ide/src/completion/completion_item.rs @@ -121,6 +121,7 @@ pub enum CompletionItemKind { Method, TypeParam, Macro, + Attribute, } #[derive(Debug, PartialEq, Eq, Copy, Clone)] @@ -134,6 +135,7 @@ pub(crate) enum CompletionKind { Snippet, Postfix, BuiltinType, + Attribute, } #[derive(Debug, PartialEq, Eq, Copy, Clone)] diff --git a/crates/rust-analyzer/src/conv.rs b/crates/rust-analyzer/src/conv.rs index 098ee369c..2285cb1d3 100644 --- a/crates/rust-analyzer/src/conv.rs +++ b/crates/rust-analyzer/src/conv.rs @@ -100,6 +100,7 @@ impl Conv for CompletionItemKind { CompletionItemKind::Method => Method, CompletionItemKind::TypeParam => TypeParameter, CompletionItemKind::Macro => Method, + CompletionItemKind::Attribute => EnumMember, } } } -- cgit v1.2.3