@@ -88,8 +88,9 @@ public struct DocumentationComment {
88
88
}
89
89
90
90
// Disable smart quotes and dash conversion since we want to preserve the original content of
91
- // the comments instead of doing documentation generation.
92
- let doc = Document ( parsing: commentInfo. text, options: [ . disableSmartOpts] )
91
+ // the comments instead of doing documentation generation. For the same reason, parse
92
+ // symbol links to preserve the double-backtick delimiters.
93
+ let doc = Document ( parsing: commentInfo. text, options: [ . disableSmartOpts, . parseSymbolLinks] )
93
94
self . init ( markup: doc)
94
95
}
95
96
@@ -129,8 +130,11 @@ public struct DocumentationComment {
129
130
130
131
extractSimpleFields ( from: & list)
131
132
132
- // If the list is now empty, don't add it to the body nodes below.
133
- guard !list. isEmpty else { continue }
133
+ // Add the list if non-empty, then `continue` so that we don't add the original node.
134
+ if !list. isEmpty {
135
+ bodyNodes. append ( list)
136
+ }
137
+ continue
134
138
}
135
139
136
140
bodyNodes. append ( child. detachedFromParent)
@@ -344,3 +348,137 @@ private struct SimpleFieldMarkupRewriter: MarkupRewriter {
344
348
return Text ( String ( nameAndRemainder [ 1 ] ) )
345
349
}
346
350
}
351
+
352
+ extension DocumentationComment {
353
+ /// Returns a trivia collection containing this documentation comment,
354
+ /// formatted and rewrapped to the given line width.
355
+ ///
356
+ /// - Parameters:
357
+ /// - lineWidth: The expected line width, including leading spaces, the
358
+ /// triple-slash prefix, and the documentation text.
359
+ /// - joiningTrivia: The trivia to put between each line of documentation
360
+ /// text. `joiningTrivia` must include a `.newlines` trivia piece.
361
+ /// - Returns: A trivia collection that represents this documentation comment
362
+ /// in standardized form.
363
+ func renderForSource( lineWidth: Int , joiningTrivia: some Collection < TriviaPiece > ) -> Trivia {
364
+ // The width of the prefix is 4 (`/// `) plus the number of spaces in `joiningTrivia`.
365
+ let prefixWidth =
366
+ 4
367
+ + joiningTrivia. map {
368
+ switch $0 {
369
+ case . spaces( let n) : n
370
+ default : 0
371
+ }
372
+ } . reduce ( 0 , + )
373
+
374
+ let options = MarkupFormatter . Options (
375
+ orderedListNumerals: . incrementing( start: 1 ) ,
376
+ preferredLineLimit: . init( maxLength: lineWidth - prefixWidth, breakWith: . softBreak)
377
+ )
378
+
379
+ var strings : [ String ] = [ ]
380
+ if let briefSummary {
381
+ strings. append (
382
+ contentsOf: briefSummary. formatForSource ( options: options)
383
+ )
384
+ }
385
+
386
+ if !bodyNodes. isEmpty {
387
+ if !strings. isEmpty { strings. append ( " " ) }
388
+
389
+ let renderedBody = bodyNodes. map {
390
+ $0. formatForSource ( options: options)
391
+ } . joined ( separator: [ " " ] )
392
+ strings. append ( contentsOf: renderedBody)
393
+ }
394
+
395
+ // Empty line between discussion and the params/returns/throws documentation.
396
+ if !strings. isEmpty && ( !parameters. isEmpty || returns != nil || `throws` != nil ) {
397
+ strings. append ( " " )
398
+ }
399
+
400
+ // FIXME: Need to recurse rather than only using the `briefSummary`
401
+ switch parameters. count {
402
+ case 0 : break
403
+ case 1 :
404
+ // Output a single parameter item.
405
+ let summary = parameters [ 0 ] . comment. briefSummary ?? Paragraph ( )
406
+ let summaryWithLabel =
407
+ summary
408
+ . prefixed ( with: " Parameter \( parameters [ 0 ] . name) : " )
409
+ let list = UnorderedList ( [ ListItem ( summaryWithLabel) ] )
410
+ strings. append ( contentsOf: list. formatForSource ( options: options) )
411
+
412
+ default :
413
+ // Build the list of parameters.
414
+ let paramItems = parameters. map { parameter in
415
+ let summary = parameter. comment. briefSummary ?? Paragraph ( )
416
+ let summaryWithLabel =
417
+ summary
418
+ . prefixed ( with: " \( parameter. name) : " )
419
+ return ListItem ( summaryWithLabel)
420
+ }
421
+ let paramList = UnorderedList ( paramItems)
422
+
423
+ // Create a list with a single item: the label, followed by the list of parameters.
424
+ let listItem = ListItem (
425
+ Paragraph ( Text ( " Parameters: " ) ) ,
426
+ paramList
427
+ )
428
+ strings. append (
429
+ contentsOf: UnorderedList ( listItem) . formatForSource ( options: options)
430
+ )
431
+ }
432
+
433
+ if let returns {
434
+ let returnsWithLabel = returns. prefixed ( with: " Returns: " )
435
+ let list = UnorderedList ( [ ListItem ( returnsWithLabel) ] )
436
+ strings. append ( contentsOf: list. formatForSource ( options: options) )
437
+ }
438
+
439
+ if let `throws` {
440
+ let throwsWithLabel = `throws`. prefixed ( with: " Throws: " )
441
+ let list = UnorderedList ( [ ListItem ( throwsWithLabel) ] )
442
+ strings. append ( contentsOf: list. formatForSource ( options: options) )
443
+ }
444
+
445
+ // Convert the pieces into trivia, then join them with the provided spacing.
446
+ let pieces = strings. map {
447
+ $0. isEmpty
448
+ ? TriviaPiece . docLineComment ( " /// " )
449
+ : TriviaPiece . docLineComment ( " /// " + $0)
450
+ }
451
+ let spacedPieces : [ TriviaPiece ] = pieces. reduce ( into: [ ] ) { result, piece in
452
+ result. append ( piece)
453
+ result. append ( contentsOf: joiningTrivia)
454
+ }
455
+
456
+ return Trivia ( pieces: spacedPieces)
457
+ }
458
+ }
459
+
460
+ extension Markup {
461
+ func formatForSource( options: MarkupFormatter . Options ) -> [ String ] {
462
+ format ( options: options)
463
+ . split ( separator: " \n " , omittingEmptySubsequences: false )
464
+ . map { $0. trimmingTrailingWhitespace ( ) }
465
+ }
466
+ }
467
+
468
+ extension Paragraph {
469
+ func prefixed( with str: String ) -> Paragraph {
470
+ struct ParagraphPrefixMarkupRewriter : MarkupRewriter {
471
+ /// The list item to which the rewriter will be applied.
472
+ let prefix : String
473
+
474
+ mutating func visitText( _ text: Text ) -> Markup ? {
475
+ // Only manipulate the first text node (of the first paragraph).
476
+ guard text. indexInParent == 0 else { return text }
477
+ return Text ( String ( prefix + text. string) )
478
+ }
479
+ }
480
+
481
+ var rewriter = ParagraphPrefixMarkupRewriter ( prefix: str)
482
+ return self . accept ( & rewriter) as? Paragraph ?? self
483
+ }
484
+ }
0 commit comments