Render documentation for derive completion

This commit is contained in:
Lukas Wirth 2021-06-04 20:25:16 +02:00
parent 98395f29a4
commit 7524850831
3 changed files with 56 additions and 40 deletions

View File

@ -36,6 +36,10 @@ use crate::{
pub struct Documentation(String); pub struct Documentation(String);
impl Documentation { impl Documentation {
pub fn new(s: impl Into<String>) -> Self {
Documentation(s.into())
}
pub fn as_str(&self) -> &str { pub fn as_str(&self) -> &str {
&self.0 &self.0
} }

View File

@ -1,6 +1,7 @@
//! Completion for derives //! Completion for derives
use hir::HasAttrs;
use itertools::Itertools; use itertools::Itertools;
use rustc_hash::FxHashSet; use rustc_hash::FxHashMap;
use syntax::ast; use syntax::ast;
use crate::{ use crate::{
@ -15,9 +16,10 @@ pub(super) fn complete_derive(
derive_input: ast::TokenTree, derive_input: ast::TokenTree,
) { ) {
if let Some(existing_derives) = super::parse_comma_sep_input(derive_input) { if let Some(existing_derives) = super::parse_comma_sep_input(derive_input) {
for derive_completion in DEFAULT_DERIVE_COMPLETIONS for (derive, docs) in get_derive_names_in_scope(ctx) {
let (label, lookup) = if let Some(derive_completion) = DEFAULT_DERIVE_COMPLETIONS
.iter() .iter()
.filter(|completion| !existing_derives.contains(completion.label)) .find(|derive_completion| derive_completion.label == derive)
{ {
let mut components = vec![derive_completion.label]; let mut components = vec![derive_completion.label];
components.extend( components.extend(
@ -28,53 +30,50 @@ pub(super) fn complete_derive(
); );
let lookup = components.join(", "); let lookup = components.join(", ");
let label = components.iter().rev().join(", "); let label = components.iter().rev().join(", ");
(label, Some(lookup))
} else {
(derive, None)
};
let mut item = let mut item =
CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label); CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label);
item.lookup_by(lookup).kind(CompletionItemKind::Attribute);
item.add_to(acc);
}
for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) {
let mut item = CompletionItem::new(
CompletionKind::Attribute,
ctx.source_range(),
custom_derive_name,
);
item.kind(CompletionItemKind::Attribute); item.kind(CompletionItemKind::Attribute);
if let Some(docs) = docs {
item.documentation(docs);
}
if let Some(lookup) = lookup {
item.lookup_by(lookup);
}
item.add_to(acc); item.add_to(acc);
} }
} }
} }
fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> { fn get_derive_names_in_scope(
let mut result = FxHashSet::default(); ctx: &CompletionContext,
) -> FxHashMap<String, Option<hir::Documentation>> {
let mut result = FxHashMap::default();
ctx.scope.process_all_names(&mut |name, scope_def| { ctx.scope.process_all_names(&mut |name, scope_def| {
if let hir::ScopeDef::MacroDef(mac) = scope_def { if let hir::ScopeDef::MacroDef(mac) = scope_def {
if mac.kind() == hir::MacroKind::Derive { if mac.kind() == hir::MacroKind::Derive {
result.insert(name.to_string()); result.insert(name.to_string(), mac.docs(ctx.db));
} }
} }
}); });
result result
} }
struct DeriveCompletion { struct DeriveDependencies {
label: &'static str, label: &'static str,
dependencies: &'static [&'static str], dependencies: &'static [&'static str],
} }
/// Standard Rust derives and the information about their dependencies /// Standard Rust derives that have dependencies
/// (the dependencies are needed so that the main derive don't break the compilation when added) /// (the dependencies are needed so that the main derive don't break the compilation when added)
const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[ const DEFAULT_DERIVE_COMPLETIONS: &[DeriveDependencies] = &[
DeriveCompletion { label: "Clone", dependencies: &[] }, DeriveDependencies { label: "Copy", dependencies: &["Clone"] },
DeriveCompletion { label: "Copy", dependencies: &["Clone"] }, DeriveDependencies { label: "Eq", dependencies: &["PartialEq"] },
DeriveCompletion { label: "Debug", dependencies: &[] }, DeriveDependencies { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
DeriveCompletion { label: "Default", dependencies: &[] }, DeriveDependencies { label: "PartialOrd", dependencies: &["PartialEq"] },
DeriveCompletion { label: "Hash", dependencies: &[] },
DeriveCompletion { label: "PartialEq", dependencies: &[] },
DeriveCompletion { label: "Eq", dependencies: &["PartialEq"] },
DeriveCompletion { label: "PartialOrd", dependencies: &["PartialEq"] },
DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
]; ];
#[cfg(test)] #[cfg(test)]
@ -94,6 +93,7 @@ mod tests {
} }
#[test] #[test]
#[ignore] // FIXME: Fixtures cant test proc-macros/derives yet as we cant specify them in fixtures
fn empty_derive() { fn empty_derive() {
check( check(
r#"#[derive($0)] struct Test;"#, r#"#[derive($0)] struct Test;"#,
@ -112,6 +112,7 @@ mod tests {
} }
#[test] #[test]
#[ignore] // FIXME: Fixtures cant test proc-macros/derives yet as we cant specify them in fixtures
fn derive_with_input() { fn derive_with_input() {
check( check(
r#"#[derive(serde::Serialize, PartialEq, $0)] struct Test;"#, r#"#[derive(serde::Serialize, PartialEq, $0)] struct Test;"#,
@ -129,6 +130,7 @@ mod tests {
} }
#[test] #[test]
#[ignore] // FIXME: Fixtures cant test proc-macros/derives yet as we cant specify them in fixtures
fn derive_with_input2() { fn derive_with_input2() {
check( check(
r#"#[derive($0 serde::Serialize, PartialEq)] struct Test;"#, r#"#[derive($0 serde::Serialize, PartialEq)] struct Test;"#,

View File

@ -24,7 +24,8 @@ pub(super) fn complete_lint(
ctx.source_range(), ctx.source_range(),
lint_completion.label, lint_completion.label,
); );
item.kind(CompletionItemKind::Attribute).detail(lint_completion.description); item.kind(CompletionItemKind::Attribute)
.documentation(hir::Documentation::new(lint_completion.description.to_owned()));
item.add_to(acc) item.add_to(acc)
} }
} }
@ -61,4 +62,13 @@ mod tests {
r#"#[allow(keyword_idents, deprecated)] struct Test;"#, r#"#[allow(keyword_idents, deprecated)] struct Test;"#,
) )
} }
#[test]
fn check_feature() {
check_edit(
"box_syntax",
r#"#[feature(box_$0)] struct Test;"#,
r#"#[feature(box_syntax)] struct Test;"#,
)
}
} }