@@ -43,21 +43,23 @@ public struct Trivia: Sendable {
4343 }
4444
4545 /// The string contents of all the comment pieces with any comments tokens trimmed.
46- public var commentValue : String {
46+ public var commentValue : String ? {
4747 var comments = [ Substring] ( )
48+ var hasBlockComment = false
49+ var hasLineComment = false
4850
4951 // Determine if all line comments have a single space
5052 lazy var allLineCommentsHaveSpace : Bool = {
51- return pieces. allSatisfy { piece in
52- switch piece {
53- case . lineComment( let text) :
54- return text. hasPrefix ( " // " )
55- case . docLineComment( let text) :
56- return text. hasPrefix ( " /// " )
57- default :
58- return true
53+ return pieces. allSatisfy { piece in
54+ switch piece {
55+ case . lineComment( let text) :
56+ return text. hasPrefix ( " // " )
57+ case . docLineComment( let text) :
58+ return text. hasPrefix ( " /// " )
59+ default :
60+ return true
61+ }
5962 }
60- }
6163 } ( )
6264
6365 // Returns a substring with leading and trailing spaces removed.
@@ -71,71 +73,105 @@ public struct Trivia: Sendable {
7173
7274 // Strips /* */ markers and aligns content by removing common indentation.
7375 func processBlockComment( _ text: Substring ) -> String {
74- var lines = text. split ( separator: " \n " , omittingEmptySubsequences: false )
75-
76- let minIndentation =
77- lines
78- . dropFirst ( )
79- . filter { !$0. isEmpty }
80- . map { $0. prefix { $0 == " " } . count }
81- . min ( ) ?? 0
82-
83- var firstLineRemoved = false ;
84- var firstLine = lines [ 0 ]
85- if trimWhitespace ( firstLine) == " /* " || trimWhitespace ( firstLine) == " /**" {
86- lines.removeFirst()
87- firstLineRemoved = true;
88- } else {
89- firstLine = firstLine.hasPrefix("/**") ? firstLine.dropFirst(3) : firstLine.dropFirst(2)
90- while firstLine.first?.isWhitespace == true {
91- firstLine = firstLine.dropFirst()
92- }
93- lines[0] = firstLine
94- }
95-
96- if let lastLine = lines.last {
97- if trimWhitespace(lastLine) == "*/" {
98- lines.removeLast()
99- } else {
100- var lastLine = lines[lines.count - 1]
101- lastLine = lastLine.hasSuffix("*/" ) ? lastLine. dropLast ( 2 ) : lastLine
102- while lastLine. last? . isWhitespace == true {
103- lastLine = lastLine. dropLast ( )
104- }
105- lines [ lines. count - 1 ] = lastLine
106- }
107- }
76+ var lines = text. split ( separator: " \n " , omittingEmptySubsequences: false )
77+
78+ let ( minSpaceIndentation, minTabIndentation) =
79+ lines
80+ . dropFirst ( )
81+ . filter { !$0. isEmpty }
82+ . reduce ( ( Int . max, Int . max) ) { ( currentMin, line) in
83+ var spaceCount = 0 , tabCount = 0
84+ var inLeadingWhitespace = true
85+
86+ for char in line {
87+ guard inLeadingWhitespace else { break }
88+
89+ switch char {
90+ case " " :
91+ spaceCount += 1
92+ case " \t " :
93+ tabCount += 1
94+ default :
95+ inLeadingWhitespace = false
96+ }
97+ }
98+
99+ return ( Swift . min ( currentMin. 0 , spaceCount) , Swift . min ( currentMin. 1 , tabCount) )
100+ }
101+
102+ var minIndentation = minSpaceIndentation == Int . max ? 0 : minSpaceIndentation
103+ minIndentation += minTabIndentation == Int . max ? 0 : minTabIndentation
104+
105+ if let first = lines. first {
106+ let prefixToDrop = first. hasPrefix ( " /**") ? 3 : 2
107+ lines[0] = first.dropFirst(prefixToDrop)
108+ }
108109
109- let unindentedLines = lines. enumerated ( ) . map { index, line -> Substring in
110- if index == 0 && firstLineRemoved == false {
111- return line
110+ var firstLineRemoved = false
111+ if trimWhitespace(lines[0]).isEmpty {
112+ lines.removeFirst()
113+ firstLineRemoved = true
114+ }
115+
116+ var unindentedLines = lines.enumerated().map { index, line -> Substring in
117+ if index == 0 && firstLineRemoved == false {
118+ return line
119+ }
120+ return line.count >= minIndentation ? line.dropFirst(minIndentation) : line
112121 }
113- return line. count >= minIndentation ? line. dropFirst ( minIndentation) : line
114- }
115122
116- return unindentedLines. joined( separator: " \n" )
123+ if let last = unindentedLines.last, last.hasSuffix("*/" ) {
124+ unindentedLines [ unindentedLines. count - 1 ] = last. dropLast ( 2 )
125+ }
126+
127+ if trimWhitespace ( unindentedLines [ unindentedLines. count - 1 ] ) . isEmpty {
128+ unindentedLines. removeLast ( )
129+ }
130+
131+ return unindentedLines. joined ( separator: " \n " )
117132 }
118133
119134 for piece in pieces {
120- switch piece {
121- case . blockComment( let text) , . docBlockComment( let text) :
122- let processedText = processBlockComment ( text [ ... ] )
123- comments. append ( processedText [ ... ] )
135+ switch piece {
136+ case . blockComment( let text) , . docBlockComment( let text) :
137+ if hasBlockComment || hasLineComment {
138+ return nil
139+ }
140+ hasBlockComment = true
141+ let processedText = processBlockComment ( text [ ... ] )
142+ comments. append ( processedText [ ... ] )
124143
125- case . lineComment( let text) :
126- let prefix = allLineCommentsHaveSpace ? " // " : " // "
127- comments. append ( text. dropFirst ( prefix. count) )
144+ case . lineComment( let text) :
145+ if hasBlockComment {
146+ return nil
147+ }
148+ hasLineComment = true
149+ let prefix = allLineCommentsHaveSpace ? " // " : " // "
150+ comments. append ( text. dropFirst ( prefix. count) )
128151
129- case . docLineComment( let text) :
130- let prefix = allLineCommentsHaveSpace ? " /// " : " /// "
131- comments. append ( text. dropFirst ( prefix. count) )
152+ case . docLineComment( let text) :
153+ if hasBlockComment {
154+ return nil
155+ }
156+ hasLineComment = true
157+ let prefix = allLineCommentsHaveSpace ? " /// " : " /// "
158+ comments. append ( text. dropFirst ( prefix. count) )
132159
133- default :
134- break
135- }
160+ default :
161+ break
162+ }
136163 }
137- return comments. joined ( separator: " \n " )
138- }
164+
165+ guard !comments. isEmpty else { return nil }
166+
167+ // If we have multiple line comments, they can be joined with newlines
168+ if hasLineComment {
169+ return comments. joined ( separator: " \n " )
170+ }
171+
172+ // In case of block comments, we should only have one
173+ return comments. first. map ( String . init)
174+ }
139175
140176 /// The length of all the pieces in this ``Trivia``.
141177 public var sourceLength : SourceLength {
0 commit comments