1111//===----------------------------------------------------------------------===//
1212
1313extension Trivia {
14- /// The contents of all the comment pieces with any comments markers removed and indentation whitespace stripped.
15- public var commentValue : String ? {
14+ /// The contents of the last doc comment piece with any comment markers removed and indentation whitespace stripped.
15+ public var docCommentValue : String ? {
1616 var comments : [ Substring ] = [ ]
1717
18- /// Keep track of whether we have seen a line or block comment trivia piece. If this `Trivia` contains both a block
19- /// and a line comment, we don't know how to concatenate them to form the comment value and thus default to
20- /// returning `nil`.
18+ /// Keep track of whether we have seen a line or block comment trivia piece.
2119 var hasBlockComment = false
22- var hasLineComment = false
2320
24- // Determine if all line comments have a space separating the `//` or `// /` comment marker and the actual comment.
21+ // Determine if all line comments have a space separating the `///` comment marker and the actual comment.
2522 lazy var allLineCommentsHaveSpace : Bool = pieces. allSatisfy { piece in
2623 switch piece {
27- case . lineComment( let text) : return text. hasPrefix ( " // " )
2824 case . docLineComment( let text) : return text. hasPrefix ( " /// " )
2925 default : return true
3026 }
3127 }
3228
33- // Strips /* */ markers and remove any common indentation between the lines in the block comment.
34- func processBlockComment( _ text: String , isDocComment : Bool ) -> String ? {
35- var lines = text. dropPrefix ( isDocComment ? " /** " : " / *").dropSuffix("*/" )
29+ // Strips /** */ markers and removes any common indentation between the lines in the block comment.
30+ func processBlockComment( _ text: String ) -> Substring ? {
31+ var lines = text. dropPrefix ( " /**").dropSuffix("*/" )
3632 . split ( omittingEmptySubsequences: false , whereSeparator: \. isNewline)
3733
38- // If the comment content starts on the same line as the `/*` marker or ends on the same line as the `*/` marker,
34+ // If the comment content starts on the same line as the `/** ` marker or ends on the same line as the `*/` marker,
3935 // it is common to separate the marker and the actual comment using spaces. Strip those spaces if they exists.
4036 // If there are non no-space characters on the first / last line, then the comment doesn't start / end on the line
4137 // with the marker, so don't do the stripping.
@@ -48,7 +44,7 @@ extension Trivia {
4844
4945 var indentation : Substring ? = nil
5046 // Find the lowest indentation that is common among all lines in the block comment. Do not consider the first line
51- // because it won't have any indentation since it starts with /*
47+ // because it won't have any indentation since it starts with /**
5248 for line in lines. dropFirst ( ) {
5349 let lineIndentation = line. prefix ( while: { $0 == " " || $0 == " \t " } )
5450 guard let previousIndentation = indentation else {
@@ -67,7 +63,7 @@ extension Trivia {
6763 var unindentedLines = [ firstLine] + lines. dropFirst ( ) . map { $0. dropPrefix ( indentation ?? " " ) }
6864
6965 // If the first line only contained the comment marker, don't include it. We don't want to start the comment value
70- // with a newline if `/*` is on its own line. Same for the end marker.
66+ // with a newline if `/** ` is on its own line. Same for the end marker.
7167 if unindentedLines. first? . allSatisfy ( { $0 == " " } ) ?? false {
7268 unindentedLines. removeFirst ( )
7369 }
@@ -76,34 +72,48 @@ extension Trivia {
7672 }
7773 // We canonicalize the line endings to `\n` here. This matches how we concatenate the different line comment
7874 // pieces using \n as well.
79- return unindentedLines. joined ( separator: " \n " )
75+ return unindentedLines. joined ( separator: " \n " ) [ ... ]
8076 }
8177
78+ var currentLineComments : [ Substring ] = [ ]
79+ var foundStop = false
8280 for piece in pieces {
8381 switch piece {
84- case . blockComment( let text) , . docBlockComment( let text) :
85- if hasBlockComment || hasLineComment {
86- return nil
82+ case . docBlockComment( let text) :
83+ if let processedComment = processBlockComment ( text) {
84+ if hasBlockComment {
85+ comments. append ( processedComment)
86+ } else {
87+ hasBlockComment = true
88+ comments = [ processedComment]
89+ }
8790 }
88- hasBlockComment = true
89- guard let processedText = processBlockComment ( text, isDocComment: piece. isDocComment) else {
90- return nil
91+ currentLineComments = [ ] // Reset line comments when encountering a block comment
92+ case . docLineComment( let text) :
93+ let prefixToDrop = ( " /// " ) + ( allLineCommentsHaveSpace ? " " : " " )
94+ if foundStop {
95+ currentLineComments = [ text. dropPrefix ( prefixToDrop) ]
96+ foundStop = false
97+ } else {
98+ currentLineComments. append ( text. dropPrefix ( prefixToDrop) )
9199 }
92- comments. append ( processedText [ ... ] )
93- case . lineComment( let text) , . docLineComment( let text) :
94- if hasBlockComment {
95- return nil
96- }
97- hasLineComment = true
98- let prefixToDrop = ( piece. isDocComment ? " /// " : " // " ) + ( allLineCommentsHaveSpace ? " " : " " )
99- comments. append ( text. dropPrefix ( prefixToDrop) )
100+ case . newlines( let count) where count == 1 :
101+ continue
100102 default :
101- break
103+ if !currentLineComments. isEmpty {
104+ comments = currentLineComments
105+ }
106+ currentLineComments = [ ]
107+ foundStop = true
102108 }
103109 }
104110
105- if comments. isEmpty { return nil }
111+ // If there are remaining line comments, use them as the last doc comment block.
112+ if !currentLineComments. isEmpty {
113+ comments = currentLineComments
114+ }
106115
116+ if comments. isEmpty { return nil }
107117 return comments. joined ( separator: " \n " )
108118 }
109119}
@@ -132,12 +142,3 @@ fileprivate extension StringProtocol where SubSequence == Substring {
132142fileprivate func commonPrefix( _ lhs: Substring , _ rhs: Substring ) -> Substring {
133143 return lhs [ ..< lhs. index ( lhs. startIndex, offsetBy: zip ( lhs, rhs) . prefix { $0 == $1 } . count) ]
134144}
135-
136- fileprivate extension TriviaPiece {
137- var isDocComment : Bool {
138- switch self {
139- case . docBlockComment, . docLineComment: return true
140- default : return false
141- }
142- }
143- }
0 commit comments