@@ -11,7 +11,7 @@ lazy_static! {
1111 /// @tag maybe_value
1212 static ref JS_DOC_TAG_WITH_MAYBE_VALUE_RE : Regex = Regex :: new( r"(?s)^\s*@(deprecated|module)(?:\s+(.+))?" ) . unwrap( ) ;
1313 /// @tag value
14- static ref JS_DOC_TAG_WITH_VALUE_RE : Regex = Regex :: new( r"(?s)^\s*@(category|group|see|example|tags|since|priority)(?:\s+(.+))" ) . unwrap( ) ;
14+ static ref JS_DOC_TAG_WITH_VALUE_RE : Regex = Regex :: new( r"(?s)^\s*@(category|group|see|example|tags|since|priority|summary|description )(?:\s+(.+))" ) . unwrap( ) ;
1515 /// @tag name maybe_value
1616 static ref JS_DOC_TAG_NAMED_WITH_MAYBE_VALUE_RE : Regex = Regex :: new( r"(?s)^\s*@(callback|template|typeparam|typeParam)\s+([a-zA-Z_$]\S*)(?:\s+(.+))?" ) . unwrap( ) ;
1717 /// @tag {type} name maybe_value
@@ -71,6 +71,7 @@ impl From<String> for JsDoc {
7171 let mut tag_is_codeblock = false ;
7272 let mut current_tag: Option < String > = None ;
7373 let mut current_tag_name = "" ;
74+ let mut description_override: Option < String > = None ;
7475 for line in value. lines ( ) {
7576 let caps = JS_DOC_TAG_RE . captures ( line) ;
7677 if is_tag || caps. is_some ( ) {
@@ -82,7 +83,15 @@ impl From<String> for JsDoc {
8283 tag_is_codeblock = false ;
8384 let current_tag = std:: mem:: take ( & mut current_tag) ;
8485 if let Some ( current_tag) = current_tag {
85- tags. push ( current_tag. into ( ) ) ;
86+ if current_tag_name == "description" {
87+ if let Some ( caps) = JS_DOC_TAG_WITH_VALUE_RE . captures ( & current_tag) {
88+ if let Some ( m) = caps. get ( 2 ) {
89+ description_override = Some ( m. as_str ( ) . to_string ( ) ) ;
90+ }
91+ }
92+ } else {
93+ tags. push ( current_tag. into ( ) ) ;
94+ }
8695 }
8796 }
8897 if let Some ( caps) = caps {
@@ -118,9 +127,22 @@ impl From<String> for JsDoc {
118127 }
119128 }
120129 if let Some ( current_tag) = current_tag {
121- tags. push ( current_tag. into ( ) ) ;
130+ if current_tag_name == "description" {
131+ if let Some ( rest) = current_tag. strip_prefix ( "@description" ) {
132+ let desc = rest. trim_start ( ) ;
133+ if !desc. is_empty ( ) {
134+ description_override = Some ( desc. to_string ( ) ) ;
135+ }
136+ }
137+ } else {
138+ tags. push ( current_tag. into ( ) ) ;
139+ }
122140 }
123- let doc = doc_lines. map ( |doc_lines| doc_lines. into_boxed_str ( ) ) ;
141+ let doc = if let Some ( desc) = description_override {
142+ Some ( desc. into_boxed_str ( ) )
143+ } else {
144+ doc_lines. map ( |doc_lines| doc_lines. into_boxed_str ( ) )
145+ } ;
124146 Self {
125147 doc,
126148 tags : tags. into_boxed_slice ( ) ,
@@ -270,6 +292,11 @@ pub enum JsDocTag {
270292 See {
271293 doc : Box < str > ,
272294 } ,
295+ /// `@summary comment`
296+ Summary {
297+ #[ serde( default ) ]
298+ doc : Box < str > ,
299+ } ,
273300 /// `@since version`
274301 Since {
275302 doc : Box < str > ,
@@ -363,6 +390,7 @@ impl From<String> for JsDocTag {
363390 } ,
364391 "see" => Self :: See { doc } ,
365392 "since" => Self :: Since { doc } ,
393+ "summary" => Self :: Summary { doc } ,
366394 "priority" => {
367395 let Ok ( priority) = doc. parse ( ) else {
368396 return Self :: Unsupported {
@@ -371,6 +399,7 @@ impl From<String> for JsDocTag {
371399 } ;
372400 Self :: Priority { priority }
373401 }
402+ "description" => unreachable ! ( "@description is handled earlier" ) ,
374403 _ => unreachable ! ( "kind unexpected: {}" , kind) ,
375404 }
376405 } else if let Some ( caps) = JS_DOC_TAG_PARAM_RE . captures ( & value) {
@@ -804,6 +833,69 @@ const a = "a";
804833
805834 } )
806835 ) ;
836+ assert_eq ! (
837+ serde_json:: to_value( JsDoc :: from(
838+ "@summary A brief summary" . to_string( )
839+ ) )
840+ . unwrap( ) ,
841+ json!( {
842+ "tags" : [ {
843+ "kind" : "summary" ,
844+ "doc" : "A brief summary" ,
845+ } ]
846+ } )
847+ ) ;
848+ }
849+
850+ #[ test]
851+ fn test_js_doc_description_tag ( ) {
852+ // @description overrides the doc field
853+ assert_eq ! (
854+ serde_json:: to_value( JsDoc :: from(
855+ "Normal doc text\n @description Override description" . to_string( )
856+ ) )
857+ . unwrap( ) ,
858+ json!( {
859+ "doc" : "Override description" ,
860+ } )
861+ ) ;
862+ // @description without preceding doc text
863+ assert_eq ! (
864+ serde_json:: to_value( JsDoc :: from(
865+ "@description The description" . to_string( )
866+ ) )
867+ . unwrap( ) ,
868+ json!( {
869+ "doc" : "The description" ,
870+ } )
871+ ) ;
872+ // @description with other tags
873+ assert_eq ! (
874+ serde_json:: to_value( JsDoc :: from(
875+ "Normal doc\n @description Override\n @param {string} a a param"
876+ . to_string( )
877+ ) )
878+ . unwrap( ) ,
879+ json!( {
880+ "doc" : "Override" ,
881+ "tags" : [ {
882+ "kind" : "param" ,
883+ "name" : "a" ,
884+ "type" : "string" ,
885+ "doc" : "a param" ,
886+ } ]
887+ } )
888+ ) ;
889+ // multi-line @description
890+ assert_eq ! (
891+ serde_json:: to_value( JsDoc :: from(
892+ "@description Line 1\n Line 2" . to_string( )
893+ ) )
894+ . unwrap( ) ,
895+ json!( {
896+ "doc" : "Line 1\n Line 2" ,
897+ } )
898+ ) ;
807899 }
808900
809901 #[ test]
@@ -1208,6 +1300,16 @@ multi-line
12081300 "type" : "Map<string, string>" ,
12091301 } )
12101302 ) ;
1303+ assert_eq ! (
1304+ serde_json:: to_value( JsDocTag :: Summary {
1305+ doc: "A brief summary" . into( ) ,
1306+ } )
1307+ . unwrap( ) ,
1308+ json!( {
1309+ "kind" : "summary" ,
1310+ "doc" : "A brief summary" ,
1311+ } )
1312+ ) ;
12111313 assert_eq ! (
12121314 serde_json:: to_value( JsDocTag :: Unsupported {
12131315 value: "unsupported" . into( )
0 commit comments