Skip to content

Commit

Permalink
[Feature] Add support for JSON output (#900)
Browse files Browse the repository at this point in the history
  • Loading branch information
danieleformichelli authored and nicklockwood committed Apr 20, 2021
1 parent 482d912 commit bb7a20f
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 9 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@ DerivedData
.swiftpm

/Snapshots/Private

# AppCode
.idea
1 change: 1 addition & 0 deletions Sources/Arguments.swift
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,7 @@ let commandLineArguments = [
"lenient",
"verbose",
"quiet",
"report",
// Misc
"help",
"version",
Expand Down
20 changes: 15 additions & 5 deletions Sources/CommandLine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ func printHelp(as type: CLI.OutputType) {
--lenient Suppress errors for unformatted code in --lint mode
--verbose Display detailed formatting output and warnings/errors
--quiet Disables non-critical output messages and warnings
--report The changes report file
SwiftFormat has a number of rules that can be enabled or disabled. By default
most rules are enabled. Use --rules to display all enabled/disabled rules.
Expand Down Expand Up @@ -289,6 +290,11 @@ func processArguments(_ args: [String], in directory: String) -> ExitCode {
print("warning: \(warning)", as: .warning)
}

// JSON output
let jsonReporter = args["report"].map {
JSONReporter(outputURL: URL(fileURLWithPath: $0))
}

// Show help
if args["help"] != nil {
printHelp(as: .content)
Expand Down Expand Up @@ -624,7 +630,7 @@ func processArguments(_ args: [String], in directory: String) -> ExitCode {
}
}
let output = try applyRules(input, options: options, lineRange: lineRange,
verbose: verbose, lint: lint)
verbose: verbose, lint: lint, jsonReporter: jsonReporter)
if let outputURL = outputURL, !useStdout {
if (try? String(contentsOf: outputURL)) != output, !dryrun {
do {
Expand Down Expand Up @@ -692,7 +698,8 @@ func processArguments(_ args: [String], in directory: String) -> ExitCode {
verbose: verbose,
dryrun: dryrun,
lint: lint,
cacheURL: cacheURL)
cacheURL: cacheURL,
jsonReporter: jsonReporter)
errors += _errors
})

Expand All @@ -704,6 +711,7 @@ func processArguments(_ args: [String], in directory: String) -> ExitCode {
print("warning: No eligible files found at \(inputPaths).", as: .warning)
}
print("SwiftFormat completed in \(time).", as: .success)
try jsonReporter?.write()
return printResult(dryrun, lint, lenient, outputFlags)
} catch {
_ = printWarnings(errors)
Expand Down Expand Up @@ -777,7 +785,7 @@ func computeHash(_ source: String) -> String {
}

func applyRules(_ source: String, options: Options, lineRange: ClosedRange<Int>?,
verbose: Bool, lint: Bool) throws -> String
verbose: Bool, lint: Bool, jsonReporter: JSONReporter?) throws -> String
{
// Parse source
var tokens = tokenize(source)
Expand All @@ -802,6 +810,7 @@ func applyRules(_ source: String, options: Options, lineRange: ClosedRange<Int>?
let updatedSource = sourceCode(for: tokens)
if lint, updatedSource != source {
changes.forEach { print($0.description, as: .warning) }
jsonReporter?.report(changes)
}
if verbose {
let rulesApplied = changes.reduce(into: Set<String>()) {
Expand All @@ -827,7 +836,8 @@ func processInput(_ inputURLs: [URL],
verbose: Bool,
dryrun: Bool,
lint: Bool,
cacheURL: URL?) -> (OutputFlags, [Error])
cacheURL: URL?,
jsonReporter: JSONReporter?) -> (OutputFlags, [Error])
{
// Load cache
let cacheDirectory = cacheURL?.deletingLastPathComponent().absoluteURL
Expand Down Expand Up @@ -914,7 +924,7 @@ func processInput(_ inputURLs: [URL],
}
} else {
output = try applyRules(input, options: options, lineRange: lineRange,
verbose: verbose, lint: lint)
verbose: verbose, lint: lint, jsonReporter: jsonReporter)
if output != input {
sourceHash = nil
}
Expand Down
5 changes: 4 additions & 1 deletion Sources/Formatter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,11 @@ public class Formatter: NSObject {
public let rule: FormatRule
public let filePath: String?

public var help: String {
return stripMarkdown(rule.help).replacingOccurrences(of: "\n", with: " ")
}

public var description: String {
let help = stripMarkdown(rule.help).replacingOccurrences(of: "\n", with: " ")
return "\(filePath ?? ""):\(line):1: warning: (\(rule.name)) \(help)"
}
}
Expand Down
68 changes: 68 additions & 0 deletions Sources/JSONReporter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//
// CommandLine.swift
// SwiftFormat
//
// Created by Daniele Formichelli on 09/04/2021.
// Copyright 2021 Nick Lockwood
//
// Distributed under the permissive MIT license
// Get the latest version from here:
//
// https://github.com/nicklockwood/SwiftFormat
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//

import Foundation

class JSONReporter {
let outputURL: URL
var changes: [Formatter.Change] = []

init(outputURL: URL) {
self.outputURL = outputURL
}

func report(_ changes: [Formatter.Change]) {
self.changes.append(contentsOf: changes)
}

func write() throws {
try JSONEncoder().encode(changes).write(to: outputURL)
}
}

extension Formatter.Change: Encodable {
enum CodingKeys: String, CodingKey {
case file
case line
case reason
case ruleID = "rule_id"
}

public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
if let filePath = self.filePath {
try container.encode(filePath, forKey: .file)
}
try container.encode(line, forKey: .line)
try container.encode(help, forKey: .reason)
try container.encode(rule.name, forKey: .ruleID)
}
}
6 changes: 6 additions & 0 deletions SwiftFormat.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@
90C4B6EB1DA4B059009EB000 /* SwiftFormat.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 90C4B6DD1DA4B059009EB000 /* SwiftFormat.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
90F16AF81DA5EB4600EB4EA1 /* FormatFileCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90F16AF71DA5EB4600EB4EA1 /* FormatFileCommand.swift */; };
90F16AFB1DA5ED9A00EB4EA1 /* CommandErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90F16AFA1DA5ED9A00EB4EA1 /* CommandErrors.swift */; };
A3DF48252620E03600F45A5F /* JSONReporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3DF48242620E03600F45A5F /* JSONReporter.swift */; };
A3DF48262620E03600F45A5F /* JSONReporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3DF48242620E03600F45A5F /* JSONReporter.swift */; };
B9C4F55C2387FA3E0088DBEE /* SupportedContentUTIs.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9C4F55B2387FA3E0088DBEE /* SupportedContentUTIs.swift */; };
E4083191202C049200CAF11D /* SwiftFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01A0EAC41D5DB54A00A0A8E3 /* SwiftFormat.swift */; };
E41CB5BF2025761D00C1BEDE /* UserSelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41CB5BE2025761D00C1BEDE /* UserSelection.swift */; };
Expand Down Expand Up @@ -223,6 +225,7 @@
90CB44D81DA4B56500F86C22 /* SwiftFormatter.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SwiftFormatter.entitlements; sourceTree = "<group>"; };
90F16AF71DA5EB4600EB4EA1 /* FormatFileCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormatFileCommand.swift; sourceTree = "<group>"; };
90F16AFA1DA5ED9A00EB4EA1 /* CommandErrors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommandErrors.swift; sourceTree = "<group>"; };
A3DF48242620E03600F45A5F /* JSONReporter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONReporter.swift; sourceTree = "<group>"; };
B9C4F55B2387FA3E0088DBEE /* SupportedContentUTIs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupportedContentUTIs.swift; sourceTree = "<group>"; };
E41CB5BE2025761D00C1BEDE /* UserSelection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSelection.swift; sourceTree = "<group>"; };
E41CB5C22026CACD00C1BEDE /* ListSelectionTableCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListSelectionTableCellView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -326,6 +329,7 @@
01A0EAA61D5DB4CF00A0A8E3 /* Sources */ = {
isa = PBXGroup;
children = (
A3DF48242620E03600F45A5F /* JSONReporter.swift */,
01F17E811E25870700DCD359 /* CommandLine.swift */,
E4E4D3C82033F17C000D7CB1 /* EnumAssociable.swift */,
01B3987C1D763493009ADE61 /* Formatter.swift */,
Expand Down Expand Up @@ -771,6 +775,7 @@
01ACAE05220CD90F003F3CCF /* Examples.swift in Sources */,
01D3B28624E9C9C700888DE0 /* FormattingHelpers.swift in Sources */,
E4E4D3C92033F17C000D7CB1 /* EnumAssociable.swift in Sources */,
A3DF48252620E03600F45A5F /* JSONReporter.swift in Sources */,
01A0EAC11D5DB4F700A0A8E3 /* Rules.swift in Sources */,
01A0EAC51D5DB54A00A0A8E3 /* SwiftFormat.swift in Sources */,
01B3987D1D763493009ADE61 /* Formatter.swift in Sources */,
Expand Down Expand Up @@ -824,6 +829,7 @@
01045A92211988F100D2BE3D /* Inference.swift in Sources */,
01F3DF8D1DB9FD3F00454944 /* Options.swift in Sources */,
E4FABAD6202FEF060065716E /* OptionDescriptor.swift in Sources */,
A3DF48262620E03600F45A5F /* JSONReporter.swift in Sources */,
01A8320724EC7F7600A9D0EB /* FormattingHelpers.swift in Sources */,
01F17E831E25870700DCD359 /* CommandLine.swift in Sources */,
01ACAE06220CD914003F3CCF /* Examples.swift in Sources */,
Expand Down
6 changes: 3 additions & 3 deletions Tests/SwiftFormatTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class SwiftFormatTests: XCTestCase {
return { files.append(inputURL) }
}
XCTAssertEqual(errors.count, 0)
XCTAssertEqual(files.count, 55)
XCTAssertEqual(files.count, 56)
}

func testInputFilesMatchOutputFilesForSameOutput() {
Expand All @@ -78,7 +78,7 @@ class SwiftFormatTests: XCTestCase {
return { files.append(inputURL) }
}
XCTAssertEqual(errors.count, 0)
XCTAssertEqual(files.count, 55)
XCTAssertEqual(files.count, 56)
}

func testInputFileNotEnumeratedWhenExcluded() {
Expand All @@ -93,7 +93,7 @@ class SwiftFormatTests: XCTestCase {
return { files.append(inputURL) }
}
XCTAssertEqual(errors.count, 0)
XCTAssertEqual(files.count, 33)
XCTAssertEqual(files.count, 34)
}

// MARK: format function
Expand Down

0 comments on commit bb7a20f

Please sign in to comment.