Skip to content

Commit 92702e9

Browse files
authored
feat(forge doc): include @Custom natspec (#9075)
* feat(`forge doc`): include @Custom natspec * chore: make clippy happy * test: implement test for `is_custom` * chore: make rustfmt happy * doc: nit * chore: format custom tags
1 parent 0c659f0 commit 92702e9

File tree

2 files changed

+61
-9
lines changed

2 files changed

+61
-9
lines changed

crates/doc/src/parser/comment.rs

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ impl Comment {
9696
},
9797
)
9898
}
99+
100+
/// Check if this comment is a custom tag.
101+
pub fn is_custom(&self) -> bool {
102+
matches!(self.tag, CommentTag::Custom(_))
103+
}
99104
}
100105

101106
/// The collection of natspec [Comment] items.
@@ -157,18 +162,18 @@ impl From<Vec<DocCommentTag>> for Comments {
157162
pub struct CommentsRef<'a>(Vec<&'a Comment>);
158163

159164
impl<'a> CommentsRef<'a> {
160-
/// Filter a collection of comments and return only those that match a provided tag
165+
/// Filter a collection of comments and return only those that match a provided tag.
161166
pub fn include_tag(&self, tag: CommentTag) -> Self {
162167
self.include_tags(&[tag])
163168
}
164169

165-
/// Filter a collection of comments and return only those that match provided tags
170+
/// Filter a collection of comments and return only those that match provided tags.
166171
pub fn include_tags(&self, tags: &[CommentTag]) -> Self {
167172
// Cloning only references here
168173
CommentsRef(self.iter().cloned().filter(|c| tags.contains(&c.tag)).collect())
169174
}
170175

171-
/// Filter a collection of comments and return only those that do not match provided tags
176+
/// Filter a collection of comments and return only those that do not match provided tags.
172177
pub fn exclude_tags(&self, tags: &[CommentTag]) -> Self {
173178
// Cloning only references here
174179
CommentsRef(self.iter().cloned().filter(|c| !tags.contains(&c.tag)).collect())
@@ -192,6 +197,11 @@ impl<'a> CommentsRef<'a> {
192197
.find(|c| matches!(c.tag, CommentTag::Inheritdoc))
193198
.and_then(|c| c.value.split_whitespace().next())
194199
}
200+
201+
/// Filter a collection of comments and only return the custom tags.
202+
pub fn get_custom_tags(&self) -> Self {
203+
CommentsRef(self.iter().cloned().filter(|c| c.is_custom()).collect())
204+
}
195205
}
196206

197207
impl<'a> From<&'a Comments> for CommentsRef<'a> {
@@ -228,4 +238,32 @@ mod tests {
228238
assert_eq!(CommentTag::from_str("custom"), None);
229239
assert_eq!(CommentTag::from_str("sometag"), None);
230240
}
241+
242+
#[test]
243+
fn test_is_custom() {
244+
// Test custom tag.
245+
let custom_comment = Comment::new(
246+
CommentTag::from_str("custom:test").unwrap(),
247+
"dummy custom tag".to_owned(),
248+
);
249+
assert!(custom_comment.is_custom(), "Custom tag should return true for is_custom");
250+
251+
// Test non-custom tags.
252+
let non_custom_tags = [
253+
CommentTag::Title,
254+
CommentTag::Author,
255+
CommentTag::Notice,
256+
CommentTag::Dev,
257+
CommentTag::Param,
258+
CommentTag::Return,
259+
CommentTag::Inheritdoc,
260+
];
261+
for tag in non_custom_tags {
262+
let comment = Comment::new(tag.clone(), "Non-custom comment".to_string());
263+
assert!(
264+
!comment.is_custom(),
265+
"Non-custom tag {tag:?} should return false for is_custom"
266+
);
267+
}
268+
}
231269
}

crates/doc/src/writer/as_doc.rs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,18 +46,32 @@ impl AsDoc for CommentsRef<'_> {
4646

4747
// Write notice tags
4848
let notices = self.include_tag(CommentTag::Notice);
49-
for notice in notices.iter() {
50-
writer.writeln_raw(&notice.value)?;
49+
for n in notices.iter() {
50+
writer.writeln_raw(&n.value)?;
5151
writer.writeln()?;
5252
}
5353

5454
// Write dev tags
5555
let devs = self.include_tag(CommentTag::Dev);
56-
for dev in devs.iter() {
57-
writer.write_italic(&dev.value)?;
56+
for d in devs.iter() {
57+
writer.write_italic(&d.value)?;
5858
writer.writeln()?;
5959
}
6060

61+
// Write custom tags
62+
let customs = self.get_custom_tags();
63+
if !customs.is_empty() {
64+
writer.write_bold(&format!("Note{}:", if customs.len() == 1 { "" } else { "s" }))?;
65+
for c in customs.iter() {
66+
writer.writeln_raw(format!(
67+
"{}{}",
68+
if customs.len() == 1 { "" } else { "- " },
69+
&c.value
70+
))?;
71+
writer.writeln()?;
72+
}
73+
}
74+
6175
Ok(writer.finish())
6276
}
6377
}
@@ -234,7 +248,7 @@ impl AsDoc for Document {
234248
func.params.iter().filter_map(|p| p.1.as_ref()).collect::<Vec<_>>();
235249
writer.try_write_param_table(CommentTag::Param, &params, &item.comments)?;
236250

237-
// Write function parameter comments in a table
251+
// Write function return parameter comments in a table
238252
let returns =
239253
func.returns.iter().filter_map(|p| p.1.as_ref()).collect::<Vec<_>>();
240254
writer.try_write_param_table(
@@ -303,7 +317,7 @@ impl Document {
303317
let params = func.params.iter().filter_map(|p| p.1.as_ref()).collect::<Vec<_>>();
304318
writer.try_write_param_table(CommentTag::Param, &params, &comments)?;
305319

306-
// Write function parameter comments in a table
320+
// Write function return parameter comments in a table
307321
let returns = func.returns.iter().filter_map(|p| p.1.as_ref()).collect::<Vec<_>>();
308322
writer.try_write_param_table(CommentTag::Return, &returns, &comments)?;
309323

0 commit comments

Comments
 (0)