Instantly share code, notes, and snippets.

@erica erica/format.swift
Last active Apr 6, 2018

Embed
What would you like to do?
import Cocoa
/// Processes text containing comment lines to fit them
/// within standard lldb 80-character width tolerance
///
/// Code samples (indented by 4 spaces), non-doc comments,
/// and paragraph breaks are preserved.
///
/// This does not yet handle keyword markup, such as for
/// `-Returns`, `-Parameter`, etc.
///
/// - Parameter lines: An array of strings that represent
/// the contents of the source Swift file.
func processCommentLines(_ lines: [String]) {
/// Fit a blob of words into a standard lldb width
/// document comment template.
///
/// - Parameter blob: A single-space delimited line of words.
/// - Parameter width: The wrapping width, set here to 78 characters.
/// - Returns: An ordered array of comment strings.
func formatCommentBlob(_ blob: String, width: Int = 78) -> [String] {
let words = blob
.components(separatedBy: .whitespaces)
.filter({ !$0.isEmpty })
var line = "/// "
var lines: [String] = []
for word in words {
// Handle very long word
if word.count > (width - 20) {
if line.count > 4 {
lines.append(line)
line = "/// "
}
lines.append("/// \(word)")
}
// Find room for this word
if line == "/// " {
line.append(word) // space is not added
} else if word.count + 1 + line.count < width {
line.append(" \(word)")
} else { // no room
lines.append(line)
line = "/// \(word)"
}
}
if line.count != 4 {
lines.append(line)
}
return lines
}
var lineIterator = lines.makeIterator()
var blob = ""
var (line, nextLine): (String?, String?) = (lineIterator.next(), lineIterator.next())
let commentLinePrefix = "/// "
let codeBlockPrefix = "/// "
while true {
// Get a line
guard let aLine = line else { return }
let thisLine = aLine.trimmingCharacters(in: .whitespaces)
// Continue until comments
guard thisLine.hasPrefix(commentLinePrefix) else {
print(thisLine)
// Advance to the next line
(line, nextLine) = (nextLine, lineIterator.next())
continue
}
// Preserve any code-indented line
guard !thisLine.hasPrefix(codeBlockPrefix) else {
print(thisLine)
// Advance to the next line
(line, nextLine) = (nextLine, lineIterator.next())
continue
}
// Fetch until code, non-comment, or para break
blob = ""
while let thisLine = line {
// Increase the blob. Trimmed lines have no spaces
// so the offset is only 3.
let idx = thisLine.index(thisLine.startIndex, offsetBy: 3)
blob += thisLine[idx...]
// Is it time to finish the blob?
if let nextLine = nextLine,
nextLine.hasPrefix(codeBlockPrefix)
|| !nextLine.hasPrefix(commentLinePrefix)
|| nextLine == "///" {
// there is a next line and it does not belong to this blob
break
}
// Advance to the next line
(line, nextLine) = (nextLine, lineIterator.next())
}
// Output blob line by line to stdout
formatCommentBlob(blob).forEach { print($0)}
// Advance to the next line
(line, nextLine) = (nextLine, lineIterator.next())
}
}
let sourceFile = #fileLiteral(resourceName: "test.swift")
let contents = try! NSString(contentsOf: sourceFile, encoding: String.Encoding.utf8.rawValue)
let lines = contents.components(separatedBy: "\n")
processCommentLines(lines)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment