diff --git a/Sources/Inference.swift b/Sources/Inference.swift index 18774a23..e4ef0907 100644 --- a/Sources/Inference.swift +++ b/Sources/Inference.swift @@ -47,15 +47,28 @@ public func inferFormatOptions(from tokens: [Token]) -> FormatOptions { } indents.append((indent, 0)) } + var previousLength = 0 formatter.forEach(.linebreak) { i, _ in - let start = formatter.startOfLine(at: i) - if case let .space(string) = formatter.tokens[start] { + if case let .space(string)? = formatter.token(at: i + 1) { if string.hasPrefix("\t") { increment("\t") } else { let length = string.count - for i in [8, 4, 3, 2, 1] where length % i == 0 { - increment(String(repeating: " ", count: i)) + let delta = previousLength - length + if delta != 0 { + switch formatter.token(at: i + 2) ?? .space("") { + case .commentBody, .delimiter(","): + return + default: + break + } + switch formatter.last(.nonSpaceOrCommentOrLinebreak, before: i) ?? .space("") { + case .delimiter(","): + break + default: + increment(String(repeating: " ", count: abs(delta))) + previousLength = length + } } } } diff --git a/SwiftFormat.xcodeproj/project.pbxproj b/SwiftFormat.xcodeproj/project.pbxproj index 5353961a..af398aa3 100644 --- a/SwiftFormat.xcodeproj/project.pbxproj +++ b/SwiftFormat.xcodeproj/project.pbxproj @@ -18,7 +18,6 @@ 01045AA0211A1EE300D2BE3D /* Arguments.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01045A982119979400D2BE3D /* Arguments.swift */; }; 010BC9A21DCFDF1E003F4F11 /* TokenizerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 018E82741D62E730008CA0F8 /* TokenizerTests.swift */; }; 012092821E2D9A3C008B0520 /* RulesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01A0EAB31D5DB4D000A0A8E3 /* RulesTests.swift */; }; - 012092851E2D9A7B008B0520 /* InferenceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01F3DF8F1DBA003E00454944 /* InferenceTests.swift */; }; 012092861E2D9E14008B0520 /* FormatterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B3987A1D763424009ADE61 /* FormatterTests.swift */; }; 0142F06F1D72FE10007D66CC /* SwiftFormatTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0142F06E1D72FE10007D66CC /* SwiftFormatTests.swift */; }; 015AF2C01DC6A538008F0A8C /* SwiftFormat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 01A0EAA41D5DB4CF00A0A8E3 /* SwiftFormat.framework */; }; @@ -687,7 +686,6 @@ files = ( 012092861E2D9E14008B0520 /* FormatterTests.swift in Sources */, 012092821E2D9A3C008B0520 /* RulesTests.swift in Sources */, - 012092851E2D9A7B008B0520 /* InferenceTests.swift in Sources */, 015AF2CA1DC6A58C008F0A8C /* PerformanceTests.swift in Sources */, 010BC9A21DCFDF1E003F4F11 /* TokenizerTests.swift in Sources */, ); diff --git a/Tests/InferenceTests.swift b/Tests/InferenceTests.swift index c980cc6a..965b7d9d 100644 --- a/Tests/InferenceTests.swift +++ b/Tests/InferenceTests.swift @@ -29,10 +29,33 @@ // SOFTWARE. // -import SwiftFormat +@testable import SwiftFormat import XCTest class InferenceTests: XCTestCase { + static let files: [String] = { + var files = [String]() + let inputURL = URL(fileURLWithPath: #file) + .deletingLastPathComponent().deletingLastPathComponent() + + _ = enumerateFiles(withInputURL: inputURL) { url, _, _ in + return { + if let source = try? String(contentsOf: url) { + files.append(source) + } + } + } + return files + }() + + func testInferOptionsForProject() { + let files = InferenceTests.files + let tokens = files.flatMap { tokenize($0) } + let options = Options(formatOptions: inferFormatOptions(from: tokens)) + let arguments = serialize(options: options, excludingDefaults: true, separator: " ") + XCTAssertEqual(arguments, "--binarygrouping none --decimalgrouping none --hexgrouping none --octalgrouping none --wraparguments afterfirst --wrapcollections beforefirst") + } + // MARK: indent func testInferIndentLevel() { @@ -40,14 +63,50 @@ class InferenceTests: XCTestCase { \t class Foo { func bar() { - //baz - //quux - //foo + baz() + quux() + let foo = Foo() } } """ let options = inferFormatOptions(from: tokenize(input)) - XCTAssertEqual(options.indent, " ") + XCTAssertEqual(options.indent.count, 4) + } + + func testInferIndentWithComment() { + let input = """ + class Foo { + /* + A multiline comment + which has unusual + indenting that + might screw up + the indent inference + */ + } + """ + let options = inferFormatOptions(from: tokenize(input)) + XCTAssertEqual(options.indent.count, 4) + } + + func testInferIndentWithWrappedFunction() { + let input = """ + class Foo { + func foo(arg: Int, + arg: Int, + arg: Int) {} + + func bar(arg: Int, + arg: Int, + arg: Int) {} + + func baz(arg: Int, + arg: Int, + arg: Int) {} + } + """ + let options = inferFormatOptions(from: tokenize(input)) + XCTAssertEqual(options.indent.count, 4) } // MARK: linebreak diff --git a/Tests/PerformanceTests.swift b/Tests/PerformanceTests.swift index 4b301e5f..d55b0aaf 100644 --- a/Tests/PerformanceTests.swift +++ b/Tests/PerformanceTests.swift @@ -102,9 +102,11 @@ class PerformanceTests: XCTestCase { func testInferring() { let files = PerformanceTests.files let tokens = files.flatMap { tokenize($0) } + var options: FormatOptions? measure { - _ = inferFormatOptions(from: tokens) + options = inferFormatOptions(from: tokens) } + XCTAssertEqual(options?.indent.count, 4) } func testIndent() {