11
11
//===----------------------------------------------------------------------===//
12
12
13
13
extension 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 ? {
16
16
var comments : [ Substring ] = [ ]
17
17
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.
21
19
var hasBlockComment = false
22
- var hasLineComment = false
23
20
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.
25
22
lazy var allLineCommentsHaveSpace : Bool = pieces. allSatisfy { piece in
26
23
switch piece {
27
- case . lineComment( let text) : return text. hasPrefix ( " // " )
28
24
case . docLineComment( let text) : return text. hasPrefix ( " /// " )
29
25
default : return true
30
26
}
31
27
}
32
28
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("*/" )
36
32
. split ( omittingEmptySubsequences: false , whereSeparator: \. isNewline)
37
33
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,
39
35
// it is common to separate the marker and the actual comment using spaces. Strip those spaces if they exists.
40
36
// If there are non no-space characters on the first / last line, then the comment doesn't start / end on the line
41
37
// with the marker, so don't do the stripping.
@@ -48,7 +44,7 @@ extension Trivia {
48
44
49
45
var indentation : Substring ? = nil
50
46
// 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 /**
52
48
for line in lines. dropFirst ( ) {
53
49
let lineIndentation = line. prefix ( while: { $0 == " " || $0 == " \t " } )
54
50
guard let previousIndentation = indentation else {
@@ -67,7 +63,7 @@ extension Trivia {
67
63
var unindentedLines = [ firstLine] + lines. dropFirst ( ) . map { $0. dropPrefix ( indentation ?? " " ) }
68
64
69
65
// 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.
71
67
if unindentedLines. first? . allSatisfy ( { $0 == " " } ) ?? false {
72
68
unindentedLines. removeFirst ( )
73
69
}
@@ -76,34 +72,48 @@ extension Trivia {
76
72
}
77
73
// We canonicalize the line endings to `\n` here. This matches how we concatenate the different line comment
78
74
// pieces using \n as well.
79
- return unindentedLines. joined ( separator: " \n " )
75
+ return unindentedLines. joined ( separator: " \n " ) [ ... ]
80
76
}
81
77
78
+ var currentLineComments : [ Substring ] = [ ]
79
+ var foundStop = false
82
80
for piece in pieces {
83
81
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
+ }
87
90
}
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) )
91
99
}
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
100
102
default :
101
- break
103
+ if !currentLineComments. isEmpty {
104
+ comments = currentLineComments
105
+ }
106
+ currentLineComments = [ ]
107
+ foundStop = true
102
108
}
103
109
}
104
110
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
+ }
106
115
116
+ if comments. isEmpty { return nil }
107
117
return comments. joined ( separator: " \n " )
108
118
}
109
119
}
@@ -132,12 +142,3 @@ fileprivate extension StringProtocol where SubSequence == Substring {
132
142
fileprivate func commonPrefix( _ lhs: Substring , _ rhs: Substring ) -> Substring {
133
143
return lhs [ ..< lhs. index ( lhs. startIndex, offsetBy: zip ( lhs, rhs) . prefix { $0 == $1 } . count) ]
134
144
}
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