mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 06:11:35 +00:00
Enable hover and autocomplete docs on macro generated items
This commit is contained in:
parent
9b3d4be421
commit
4c655c01f3
5 changed files with 110 additions and 10 deletions
|
@ -87,12 +87,18 @@ impl Attrs {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs {
|
pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs {
|
||||||
|
let docs = ast::CommentIter::from_syntax_node(owner.syntax()).doc_comment_text().map(
|
||||||
|
|docs_text| Attr {
|
||||||
|
input: Some(AttrInput::Literal(SmolStr::new(docs_text))),
|
||||||
|
path: ModPath::from(hir_expand::name!(doc)),
|
||||||
|
},
|
||||||
|
);
|
||||||
let mut attrs = owner.attrs().peekable();
|
let mut attrs = owner.attrs().peekable();
|
||||||
let entries = if attrs.peek().is_none() {
|
let entries = if attrs.peek().is_none() {
|
||||||
// Avoid heap allocation
|
// Avoid heap allocation
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(attrs.flat_map(|ast| Attr::from_src(ast, hygiene)).collect())
|
Some(attrs.flat_map(|ast| Attr::from_src(ast, hygiene)).chain(docs).collect())
|
||||||
};
|
};
|
||||||
Attrs { entries }
|
Attrs { entries }
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,45 @@ impl Documentation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn docs_from_ast(node: &impl ast::DocCommentsOwner) -> Option<Documentation> {
|
pub(crate) fn docs_from_ast<N>(node: &N) -> Option<Documentation>
|
||||||
node.doc_comment_text().map(|it| Documentation::new(&it))
|
where
|
||||||
|
N: ast::DocCommentsOwner + ast::AttrsOwner,
|
||||||
|
{
|
||||||
|
let doc_comment_text = node.doc_comment_text();
|
||||||
|
let doc_attr_text = expand_doc_attrs(node);
|
||||||
|
let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text);
|
||||||
|
docs.map(|it| Documentation::new(&it))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn merge_doc_comments_and_attrs(
|
||||||
|
doc_comment_text: Option<String>,
|
||||||
|
doc_attr_text: Option<String>,
|
||||||
|
) -> Option<String> {
|
||||||
|
match (doc_comment_text, doc_attr_text) {
|
||||||
|
(Some(mut comment_text), Some(attr_text)) => {
|
||||||
|
comment_text.push_str("\n\n");
|
||||||
|
comment_text.push_str(&attr_text);
|
||||||
|
Some(comment_text)
|
||||||
|
}
|
||||||
|
(Some(comment_text), None) => Some(comment_text),
|
||||||
|
(None, Some(attr_text)) => Some(attr_text),
|
||||||
|
(None, None) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expand_doc_attrs(owner: &dyn ast::AttrsOwner) -> Option<String> {
|
||||||
|
let mut docs = String::new();
|
||||||
|
for attr in owner.attrs() {
|
||||||
|
if let Some(("doc", value)) =
|
||||||
|
attr.as_simple_key_value().as_ref().map(|(k, v)| (k.as_str(), v.as_str()))
|
||||||
|
{
|
||||||
|
docs.push_str(value);
|
||||||
|
docs.push_str("\n\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if docs.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(docs)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,6 +153,7 @@ pub mod known {
|
||||||
str,
|
str,
|
||||||
// Special names
|
// Special names
|
||||||
macro_rules,
|
macro_rules,
|
||||||
|
doc,
|
||||||
// Components of known path (value or mod name)
|
// Components of known path (value or mod name)
|
||||||
std,
|
std,
|
||||||
core,
|
core,
|
||||||
|
|
|
@ -169,13 +169,19 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
|
||||||
return match def {
|
return match def {
|
||||||
Definition::Macro(it) => {
|
Definition::Macro(it) => {
|
||||||
let src = it.source(db);
|
let src = it.source(db);
|
||||||
hover_text(src.value.doc_comment_text(), Some(macro_label(&src.value)), mod_path)
|
let doc_comment_text = src.value.doc_comment_text();
|
||||||
|
let doc_attr_text = expand_doc_attrs(&src.value);
|
||||||
|
let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text);
|
||||||
|
hover_text(docs, Some(macro_label(&src.value)), mod_path)
|
||||||
}
|
}
|
||||||
Definition::Field(it) => {
|
Definition::Field(it) => {
|
||||||
let src = it.source(db);
|
let src = it.source(db);
|
||||||
match src.value {
|
match src.value {
|
||||||
FieldSource::Named(it) => {
|
FieldSource::Named(it) => {
|
||||||
hover_text(it.doc_comment_text(), it.short_label(), mod_path)
|
let doc_comment_text = it.doc_comment_text();
|
||||||
|
let doc_attr_text = expand_doc_attrs(&it);
|
||||||
|
let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text);
|
||||||
|
hover_text(docs, it.short_label(), mod_path)
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
@ -183,7 +189,10 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
|
||||||
Definition::ModuleDef(it) => match it {
|
Definition::ModuleDef(it) => match it {
|
||||||
ModuleDef::Module(it) => match it.definition_source(db).value {
|
ModuleDef::Module(it) => match it.definition_source(db).value {
|
||||||
ModuleSource::Module(it) => {
|
ModuleSource::Module(it) => {
|
||||||
hover_text(it.doc_comment_text(), it.short_label(), mod_path)
|
let doc_comment_text = it.doc_comment_text();
|
||||||
|
let doc_attr_text = expand_doc_attrs(&it);
|
||||||
|
let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text);
|
||||||
|
hover_text(docs, it.short_label(), mod_path)
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
|
@ -208,10 +217,46 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
|
||||||
fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<String>
|
fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<String>
|
||||||
where
|
where
|
||||||
D: HasSource<Ast = A>,
|
D: HasSource<Ast = A>,
|
||||||
A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel,
|
A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel + ast::AttrsOwner,
|
||||||
{
|
{
|
||||||
let src = def.source(db);
|
let src = def.source(db);
|
||||||
hover_text(src.value.doc_comment_text(), src.value.short_label(), mod_path)
|
let doc_comment_text = src.value.doc_comment_text();
|
||||||
|
let doc_attr_text = expand_doc_attrs(&src.value);
|
||||||
|
let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text);
|
||||||
|
hover_text(docs, src.value.short_label(), mod_path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn merge_doc_comments_and_attrs(
|
||||||
|
doc_comment_text: Option<String>,
|
||||||
|
doc_attr_text: Option<String>,
|
||||||
|
) -> Option<String> {
|
||||||
|
match (doc_comment_text, doc_attr_text) {
|
||||||
|
(Some(mut comment_text), Some(attr_text)) => {
|
||||||
|
comment_text.push_str("\n\n");
|
||||||
|
comment_text.push_str(&attr_text);
|
||||||
|
Some(comment_text)
|
||||||
|
}
|
||||||
|
(Some(comment_text), None) => Some(comment_text),
|
||||||
|
(None, Some(attr_text)) => Some(attr_text),
|
||||||
|
(None, None) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expand_doc_attrs(owner: &dyn ast::AttrsOwner) -> Option<String> {
|
||||||
|
let mut docs = String::new();
|
||||||
|
for attr in owner.attrs() {
|
||||||
|
if let Some(("doc", value)) =
|
||||||
|
attr.as_simple_key_value().as_ref().map(|(k, v)| (k.as_str(), v.as_str()))
|
||||||
|
{
|
||||||
|
docs.push_str(value);
|
||||||
|
docs.push_str("\n\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if docs.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(docs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -83,13 +83,22 @@ pub trait DocCommentsOwner: AstNode {
|
||||||
CommentIter { iter: self.syntax().children_with_tokens() }
|
CommentIter { iter: self.syntax().children_with_tokens() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn doc_comment_text(&self) -> Option<String> {
|
||||||
|
self.doc_comments().doc_comment_text()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CommentIter {
|
||||||
|
pub fn from_syntax_node(syntax_node: &ast::SyntaxNode) -> CommentIter {
|
||||||
|
CommentIter { iter: syntax_node.children_with_tokens() }
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the textual content of a doc comment block as a single string.
|
/// Returns the textual content of a doc comment block as a single string.
|
||||||
/// That is, strips leading `///` (+ optional 1 character of whitespace),
|
/// That is, strips leading `///` (+ optional 1 character of whitespace),
|
||||||
/// trailing `*/`, trailing whitespace and then joins the lines.
|
/// trailing `*/`, trailing whitespace and then joins the lines.
|
||||||
fn doc_comment_text(&self) -> Option<String> {
|
pub fn doc_comment_text(self) -> Option<String> {
|
||||||
let mut has_comments = false;
|
let mut has_comments = false;
|
||||||
let docs = self
|
let docs = self
|
||||||
.doc_comments()
|
|
||||||
.filter(|comment| comment.kind().doc.is_some())
|
.filter(|comment| comment.kind().doc.is_some())
|
||||||
.map(|comment| {
|
.map(|comment| {
|
||||||
has_comments = true;
|
has_comments = true;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue