@@ -43,21 +43,23 @@ public struct Trivia: Sendable {
43
43
}
44
44
45
45
/// The string contents of all the comment pieces with any comments tokens trimmed.
46
- public var commentValue : String {
46
+ public var commentValue : String ? {
47
47
var comments = [ Substring] ( )
48
+ var hasBlockComment = false
49
+ var hasLineComment = false
48
50
49
51
// Determine if all line comments have a single space
50
52
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
+ }
59
62
}
60
- }
61
63
} ( )
62
64
63
65
// Returns a substring with leading and trailing spaces removed.
@@ -71,71 +73,105 @@ public struct Trivia: Sendable {
71
73
72
74
// Strips /* */ markers and aligns content by removing common indentation.
73
75
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
+ }
108
109
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
112
121
}
113
- return line. count >= minIndentation ? line. dropFirst ( minIndentation) : line
114
- }
115
122
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 " )
117
132
}
118
133
119
134
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 [ ... ] )
124
143
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) )
128
151
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) )
132
159
133
- default :
134
- break
135
- }
160
+ default :
161
+ break
162
+ }
136
163
}
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
+ }
139
175
140
176
/// The length of all the pieces in this ``Trivia``.
141
177
public var sourceLength : SourceLength {
0 commit comments