From e08161c790e9ca86da762b53cc7153547c69843e Mon Sep 17 00:00:00 2001 From: Gustavo Shigueo Date: Thu, 25 Jul 2024 15:20:38 -0300 Subject: [PATCH] Fix JSDoc formatting for Rust block comments (#342) --- macros/src/utils.rs | 46 +++++++++++++++++++++------------ ts-rs/tests/integration/docs.rs | 44 ++++++++++++++++++++++++++++--- 2 files changed, 70 insertions(+), 20 deletions(-) diff --git a/macros/src/utils.rs b/macros/src/utils.rs index 07ce47a2..445487ff 100644 --- a/macros/src/utils.rs +++ b/macros/src/utils.rs @@ -3,8 +3,8 @@ use std::collections::HashMap; use proc_macro2::{Ident, TokenStream}; use quote::quote; use syn::{ - spanned::Spanned, Attribute, Error, Expr, ExprLit, GenericParam, Generics, Lit, Meta, Path, - Result, Type, + spanned::Spanned, Attribute, Error, Expr, ExprLit, GenericParam, Generics, Lit, Path, Result, + Type, }; use super::attr::{Attr, Serde}; @@ -140,30 +140,42 @@ where /// Return doc comments parsed and formatted as JSDoc. pub fn parse_docs(attrs: &[Attribute]) -> Result { - let lines = attrs + let doc_attrs = attrs .iter() - .filter_map(|a| match a.meta { - Meta::NameValue(ref x) if x.path.is_ident("doc") => Some(x), - _ => None, - }) + .filter_map(|attr| attr.meta.require_name_value().ok()) + .filter(|attr| attr.path.is_ident("doc")) .map(|attr| match attr.value { Expr::Lit(ExprLit { lit: Lit::Str(ref str), .. }) => Ok(str.value()), - _ => syn_err!(attr.span(); "doc attribute with non literal expression found"), - }) - .map(|attr| { - attr.map(|line| match line.trim() { - "" => " *".to_owned(), - _ => format!(" *{}", line.trim_end()), - }) + _ => syn_err!(attr.span(); "doc with non literal expression found"), }) .collect::>>()?; - Ok(match lines.is_empty() { - true => "".to_owned(), - false => format!("/**\n{}\n */\n", lines.join("\n")), + Ok(match doc_attrs.len() { + // No docs + 0 => String::new(), + + // Multi-line block doc comment (/** ... */) + 1 if doc_attrs[0].contains('\n') => format!("/**{}*/\n", &doc_attrs[0]), + + // Regular doc comment(s) (///) or single line block doc comment + _ => { + let mut buffer = String::from("/**\n"); + let mut lines = doc_attrs.iter().peekable(); + + while let Some(line) = lines.next() { + buffer.push_str(" *"); + buffer.push_str(line); + + if lines.peek().is_some() { + buffer.push('\n'); + } + } + buffer.push_str("\n */\n"); + buffer + } }) } diff --git a/ts-rs/tests/integration/docs.rs b/ts-rs/tests/integration/docs.rs index 5fec1b92..9c5ebc07 100644 --- a/ts-rs/tests/integration/docs.rs +++ b/ts-rs/tests/integration/docs.rs @@ -4,8 +4,6 @@ use std::{concat, fs}; use ts_rs::TS; -/* ============================================================================================== */ - /// Doc comment. /// Supports new lines. /// @@ -93,7 +91,16 @@ struct G { f: F, } -/* ============================================================================================== */ +#[derive(TS)] +#[ts(export_to = "docs/")] +/** + * Block doc comment + * + * works + */ +struct H { + foo: i32, +} #[test] fn export_a() { @@ -387,3 +394,34 @@ fn export_g() { assert_eq!(actual_content, expected_content); } + +#[test] +fn export_h() { + H::export().unwrap(); + + let expected_content = if cfg!(feature = "format") { + concat!( + "// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.\n\n", + "/**\n", + " * Block doc comment\n", + " *\n", + " * works\n", + " */\n", + "export type H = { foo: number };\n", + ) + } else { + concat!( + "// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.\n\n", + "/**\n", + " * Block doc comment\n", + " *\n", + " * works\n", + " */\n", + "export type H = { foo: number, };\n", + ) + }; + + let actual_content = fs::read_to_string(H::default_output_path().unwrap()).unwrap(); + + assert_eq!(actual_content, expected_content); +}