Merge #9140
9140: feat: Render documentation for derive completion r=Veykril a=Veykril ![eEzGiq2wNa](https://user-images.githubusercontent.com/3757771/120847308-9c5a3300-c573-11eb-958d-e0f22f4757ed.gif) Nothing fancy as all the std derives aren't really documented though maybe some 3rd party crates document them equally to their trait counterparts. Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
commit
ad9234fef2
@ -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
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
//! This module uses a bit of static metadata to provide completions
|
//! This module uses a bit of static metadata to provide completions
|
||||||
//! for built-in attributes.
|
//! for built-in attributes.
|
||||||
|
|
||||||
|
use hir::HasAttrs;
|
||||||
use ide_db::helpers::generated_lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES};
|
use ide_db::helpers::generated_lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
@ -81,6 +82,24 @@ fn complete_new_attribute(acc: &mut Completions, ctx: &CompletionContext, attrib
|
|||||||
None if is_inner => ATTRIBUTES.iter().for_each(add_completion),
|
None if is_inner => ATTRIBUTES.iter().for_each(add_completion),
|
||||||
None => ATTRIBUTES.iter().filter(|compl| !compl.prefer_inner).for_each(add_completion),
|
None => ATTRIBUTES.iter().filter(|compl| !compl.prefer_inner).for_each(add_completion),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: write a test for this when we can
|
||||||
|
ctx.scope.process_all_names(&mut |name, scope_def| {
|
||||||
|
if let hir::ScopeDef::MacroDef(mac) = scope_def {
|
||||||
|
if mac.kind() == hir::MacroKind::Attr {
|
||||||
|
let mut item = CompletionItem::new(
|
||||||
|
CompletionKind::Attribute,
|
||||||
|
ctx.source_range(),
|
||||||
|
name.to_string(),
|
||||||
|
);
|
||||||
|
item.kind(CompletionItemKind::Attribute);
|
||||||
|
if let Some(docs) = mac.docs(ctx.sema.db) {
|
||||||
|
item.documentation(docs);
|
||||||
|
}
|
||||||
|
acc.add(item.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AttrCompletion {
|
struct AttrCompletion {
|
||||||
|
@ -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,66 +16,64 @@ 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) {
|
||||||
.iter()
|
let (label, lookup) = if let Some(derive_completion) = DEFAULT_DERIVE_COMPLETIONS
|
||||||
.filter(|completion| !existing_derives.contains(completion.label))
|
.iter()
|
||||||
{
|
.find(|derive_completion| derive_completion.label == derive)
|
||||||
let mut components = vec![derive_completion.label];
|
{
|
||||||
components.extend(
|
let mut components = vec![derive_completion.label];
|
||||||
derive_completion
|
components.extend(
|
||||||
.dependencies
|
derive_completion
|
||||||
.iter()
|
.dependencies
|
||||||
.filter(|&&dependency| !existing_derives.contains(dependency)),
|
.iter()
|
||||||
);
|
.filter(|&&dependency| !existing_derives.contains(dependency)),
|
||||||
let lookup = components.join(", ");
|
);
|
||||||
let label = components.iter().rev().join(", ");
|
let lookup = components.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;"#,
|
||||||
|
@ -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;"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user