Skip to content

Instantly share code, notes, and snippets.

@regexident
Forked from harlanhaskins/swift-format.swift
Created October 27, 2017 11:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save regexident/8394fa2e58bdc4ee634d5228a3f2ad0f to your computer and use it in GitHub Desktop.
Save regexident/8394fa2e58bdc4ee634d5228a3f2ad0f to your computer and use it in GitHub Desktop.
Simple Swift Formatter using SwiftSyntax
import Foundation
import SwiftSyntax
func main() throws {
guard CommandLine.arguments.count > 1 else {
print("usage: swift-format [file]")
exit(-1)
}
let url = URL(fileURLWithPath: CommandLine.arguments[1])
let file = try Syntax.parse(url)
let bracesFixed = FixBraces().visit(file)
let equalsFixed = FixEqualsSpacing().visit(bracesFixed)
let semiclonsRemoved = RemoveSemicolons().visit(equalsFixed)
try "\(semiclonsRemoved)".write(to: url, atomically: true,
encoding: .utf8)
}
/// Determines if a token has a newline in its leading trivia.
func hasLeadingNewline(_ token: TokenSyntax) -> Bool {
for piece in token.leadingTrivia {
if case .newlines(_) = piece { return true }
}
return false
}
/// Ensures all opening braces and brackets appear on the same line
/// as the previous token.
class FixBraces: SyntaxRewriter {
override func visit(_ token: TokenSyntax) -> Syntax {
switch token.tokenKind {
case .leftBrace where hasLeadingNewline(token):
return token.withLeadingTrivia(.spaces(1))
default:
return token
}
}
}
/// Ensure all equal signs have 1 space before and after them.
class FixEqualsSpacing: SyntaxRewriter {
override func visit(_ token: TokenSyntax) -> Syntax {
// The child after this one, if there is one.
let sibling = token.parent?.child(at: token.indexInParent + 1)
// If the next token is an equals, then replace this trailing trivia
// with a single space.
if let nextToken = sibling as? TokenSyntax,
case .equal = nextToken.tokenKind {
return token.withTrailingTrivia(.spaces(1))
}
// Otherwise, if this token is an equals, replace its trailing trivia
// with one space.
if case .equal = token.tokenKind {
return token.withTrailingTrivia(.spaces(1))
}
return token
}
}
/// Removes all unnecessary semicolons (semicolons that end a line in
/// a scope).
class RemoveSemicolons: SyntaxRewriter {
func shouldRemoveSemicolon(_ token: TokenSyntax) -> Bool {
guard case .semicolon = token.tokenKind else {
return false
}
let nextToken = token.parent?.child(at: token.indexInParent + 1)
if let sibling = nextToken as? TokenSyntax,
sibling.tokenKind != .rightBrace {
return hasLeadingNewline(sibling)
}
return true
}
override func visit(_ token: TokenSyntax) -> TokenSyntax {
if shouldRemoveSemicolon(token) {
return SyntaxFactory.makeIdentifier("")
}
return token
}
}
try main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment