Skip to content

Commit

Permalink
Breakdown comment blocks with new lines
Browse files Browse the repository at this point in the history
This also keeps comment lines within comment blocks with their leading
comment markers in tact
  • Loading branch information
adammcarter committed Mar 29, 2024
1 parent e8cd9f9 commit 36df0fc
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 62 deletions.
111 changes: 56 additions & 55 deletions Sources/SwiftSyntax/Trivia.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,62 @@ public struct Trivia: Sendable {
///
/// Each element in the array is the trimmed contents of a line comment, or, in the case of a multi-line comment a trimmed, concatenated single string.
public var commentValues: [String] {
[
sanitizedLineCommentValues,
sanitizedBlockCommentValues,
].flatMap { $0 }
var comments = [String]()
var partialComments = [String]()

var foundStartOfCodeBlock = false
var foundEndOfCodeBlock = false
var isInCodeBlock: Bool { foundStartOfCodeBlock && !foundEndOfCodeBlock }

for piece in pieces {
switch piece {
case .blockComment(let text), .docBlockComment(let text):
let text = text.trimmingCharacters(in: "\n")

foundStartOfCodeBlock = text.hasPrefix("/*")
foundEndOfCodeBlock = text.hasSuffix("*/")

let sanitized =
text
.split(separator: "\n")
.map { $0.trimmingAnyCharacters(in: "/*").trimmingAnyCharacters(in: " ") }
.filter { !$0.isEmpty }
.joined(separator: " ")

appendPartialCommentIfPossible(sanitized)

case .lineComment(let text), .docLineComment(let text):
if isInCodeBlock {
appendPartialCommentIfPossible(text)
} else {
comments.append(String(text.trimmingPrefix("/ ")))
}

default:
break
}

if foundEndOfCodeBlock, !partialComments.isEmpty {
appendSubstringsToLines()
partialComments.removeAll()
}
}

if !partialComments.isEmpty {
appendSubstringsToLines()
}

func appendPartialCommentIfPossible(_ text: String) {
guard partialComments.isEmpty || !text.isEmpty else { return }

partialComments.append(text)
}

func appendSubstringsToLines() {
comments.append(partialComments.joined(separator: " "))
}

return comments
}

/// The length of all the pieces in this ``Trivia``.
Expand Down Expand Up @@ -225,54 +277,3 @@ extension RawTriviaPiece: CustomDebugStringConvertible {
TriviaPiece(raw: self).debugDescription
}
}

private extension Trivia {
var sanitizedLineCommentValues: [String] {
compactMap {
switch $0 {
case .lineComment(let text), .docLineComment(let text):
String(sanitizingLineComment(text))

default:
nil
}
}
}

func sanitizingLineComment(_ text: String) -> Substring {
text.trimmingPrefix("/ ")
}

var sanitizedBlockCommentValues: [String] {
var lines = [String]()
var substrings = [Substring]()
var foundTerminator = false

for piece in self {
switch piece {
case .blockComment(let text), .docBlockComment(let text):
let sanitized = text.trimmingCharacters(in: "/* ")

if substrings.isEmpty || sanitized.isEmpty == false {
substrings.append(sanitized)
}

foundTerminator = text.hasSuffix("*/")

default:
break
}

if foundTerminator, substrings.isEmpty == false {
lines.append(substrings.joined(separator: " "))
substrings.removeAll()
}
}

if substrings.isEmpty == false {
lines.append(substrings.joined(separator: " "))
}

return lines
}
}
14 changes: 9 additions & 5 deletions Sources/SwiftSyntax/Utils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -124,24 +124,28 @@ extension RawUnexpectedNodesSyntax {
extension String {
func trimmingCharacters(in charactersToTrim: any BidirectionalCollection<Character>) -> Substring {
// TODO: adammcarter - this feels a bit dirty
self[startIndex...].trimmingPrefix(charactersToTrim).trimmingSuffix(charactersToTrim)
self[startIndex...].trimmingAnyCharacters(in: charactersToTrim)
}

func trimmingPrefix(_ charactersToTrim: any BidirectionalCollection<Character>) -> Substring {
self[startIndex...].trimmingPrefix(charactersToTrim)
self[startIndex...].trimmingAnyCharactersFromPrefix(in: charactersToTrim)
}

func trimmingSuffix(_ charactersToTrim: any BidirectionalCollection<Character>) -> Substring {
self[startIndex...].trimmingSuffix(charactersToTrim)
self[startIndex...].trimmingAnyCharactersFromSuffix(in: charactersToTrim)
}
}

extension Substring {
func trimmingPrefix(_ charactersToTrim: any BidirectionalCollection<Character>) -> Self {
func trimmingAnyCharacters(in charactersToTrim: any BidirectionalCollection<Character>) -> Substring {
trimmingAnyCharactersFromPrefix(in: charactersToTrim).trimmingAnyCharactersFromSuffix(in: charactersToTrim)
}

func trimmingAnyCharactersFromPrefix(in charactersToTrim: any BidirectionalCollection<Character>) -> Self {
dropFirst(countOfSequentialCharacters(charactersToTrim, in: self))
}

func trimmingSuffix(_ charactersToTrim: any BidirectionalCollection<Character>) -> Self {
func trimmingAnyCharactersFromSuffix(in charactersToTrim: any BidirectionalCollection<Character>) -> Self {
dropLast(countOfSequentialCharacters(charactersToTrim, in: reversed()))
}
}
Expand Down
114 changes: 112 additions & 2 deletions Tests/SwiftSyntaxTest/TriviaTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,34 @@ class TriviaTests: XCTestCase {
]
)

XCTAssertEqual(
Trivia(pieces: [
.blockComment(
"""
/*
Some block comment
spread on many lines
*/
"""
)
]).commentValues,
["Some block comment spread on many lines"]
)

XCTAssertEqual(
Trivia(pieces: [
.blockComment(
"""
/*
* Some block comment
* spread on many lines
*/
"""
)
]).commentValues,
["Some block comment spread on many lines"]
)

// MARK: doc block comment

XCTAssertEqual(
Expand Down Expand Up @@ -277,7 +305,89 @@ class TriviaTests: XCTestCase {
]
)

// TODO: adammcarter - mixing trivia of lines and blocks
// TODO: adammcarter - newline chars in the prefix/suffix of code block too
XCTAssertEqual(
Trivia(pieces: [
.docBlockComment(
"""
/**
Some doc block comment
spread on many lines
*/
"""
)
]).commentValues,
["Some doc block comment spread on many lines"]
)

XCTAssertEqual(
Trivia(pieces: [
.docBlockComment(
"""
/**
* Some doc block comment
* spread on many lines
*/
"""
)
]).commentValues,
["Some doc block comment spread on many lines"]
)

// MARK: Mixing comment styles

XCTAssertEqual(
Trivia(pieces: [
.docBlockComment(
"""
/**
* Some doc block comment
* // spread on many lines
* with a line comment
*/
"""
)
]).commentValues,
["Some doc block comment // spread on many lines with a line comment"]
)

XCTAssertEqual(
Trivia(pieces: [
.docBlockComment("/** Some doc block comment"),
.docBlockComment("* spread on many lines */"),
.newlines(2),
.docLineComment("/// Some doc line comment"),
.docLineComment("// Some line comment"),
.newlines(2),
.spaces(4),
.blockComment("/* Some block comment"),
.blockComment("* spread on many lines */"),
.newlines(2),
.docBlockComment("/** Another doc block comment */"),
]).commentValues,
[
"Some doc block comment spread on many lines",
"Some doc line comment",
"Some line comment",
"Some block comment spread on many lines",
"Another doc block comment",
]
)

XCTAssertEqual(
Trivia(pieces: [
.docBlockComment("/* Some block comment"),
.docLineComment("// A line comment in a block"),
.docBlockComment("* spread on many lines */"),
.newlines(2),
.blockComment("/** Some doc block comment"),
.docLineComment("/// A doc line comment in a block"),
.blockComment("* spread on"),
.blockComment("* many lines */"),
]).commentValues,
[
"Some block comment // A line comment in a block spread on many lines",
"Some doc block comment /// A doc line comment in a block spread on many lines",
]
)
}
}

0 comments on commit 36df0fc

Please sign in to comment.