Support the new line when adding another doc comment

This commit is contained in:
TheAwiteb 2023-08-29 00:22:16 +03:00
parent d1ac816642
commit 6fbe7154ba
No known key found for this signature in database
GPG key ID: ABF818BD15DC2D34

View file

@ -6,7 +6,7 @@ use crate::{
Result, Result,
}; };
use proc_macro2::Span; use proc_macro2::{Span, TokenTree};
use quote::quote_spanned; use quote::quote_spanned;
use syn::{parse::Parser, spanned::Spanned, Attribute}; use syn::{parse::Parser, spanned::Spanned, Attribute};
@ -49,24 +49,32 @@ enum CommandAttrKind {
impl CommandAttrs { impl CommandAttrs {
pub fn from_attributes(attributes: &[Attribute]) -> Result<Self> { pub fn from_attributes(attributes: &[Attribute]) -> Result<Self> {
use CommandAttrKind::*; use CommandAttrKind::*;
// Convert the `doc` attribute into `command(description = "...")`
let attributes = attributes.iter().map(|attr| { let docs = attributes
if attr.path.is_ident("doc") { .iter()
// Extract the token literal from the doc attribute. .filter_map(|attr| parse_doc_comment(attr).map(|doc| (doc, attr.span())));
let description = parse_doc_comment(attr).unwrap_or_default();
// Convert the doc attribute into a command description attribute. let mut attributes = attributes.to_vec();
let sp = attr.span(); if docs.clone().count() != 0 {
let attr = Attribute::parse_outer if let Some(des_sp) = get_descripation_span(&attributes) {
.parse2(quote_spanned!(sp => #[command(description = #description)])) return Err(compile_error_at(
.unwrap(); "You cannot use doc comments and #[command(description = \"...\")] \
attr.into_iter().next().unwrap() simultaneously\nYou can use only one of them",
} else { des_sp,
attr.clone() ));
}
let description = docs.clone().map(|(doc, _)| doc).collect::<Vec<_>>().join("\n");
let sp = docs
.map(|(_, sp)| sp)
.reduce(|acc, sp| acc.join(sp).expect("The spans are in the same file"))
.expect("There is at least one doc comment");
let attr = Attribute::parse_outer
.parse2(quote_spanned! { sp => #[command(description = #description)] })?;
attributes.push(attr.into_iter().next().unwrap());
} }
});
fold_attrs( fold_attrs(
attributes, attributes.into_iter(),
is_command_attribute, is_command_attribute,
CommandAttr::parse, CommandAttr::parse,
Self { Self {
@ -141,6 +149,24 @@ fn is_command_attribute(a: &Attribute) -> bool {
} }
} }
/// Returns the span of the first attribute with a description meta item.
fn get_descripation_span(attrs: &[Attribute]) -> Option<Span> {
for attr in attrs {
for token in attr.tokens.clone() {
if let TokenTree::Group(group) = token {
for token in group.stream() {
if let TokenTree::Ident(ident) = token {
if ident == "description" {
return Some(group.span());
}
}
}
}
}
}
None
}
fn parse_doc_comment(attr: &Attribute) -> Option<String> { fn parse_doc_comment(attr: &Attribute) -> Option<String> {
#[allow(clippy::collapsible_match)] #[allow(clippy::collapsible_match)]
if let syn::Meta::NameValue(syn::MetaNameValue { lit, .. }) = attr.parse_meta().ok()? { if let syn::Meta::NameValue(syn::MetaNameValue { lit, .. }) = attr.parse_meta().ok()? {