Skip to content

Commit 03c5dec

Browse files
authored
Merge pull request swiftlang#140 from Trzyipolkostkicukru/finding-config
Expose finding ".swift-format" file
2 parents 44cfdf8 + 6585672 commit 03c5dec

File tree

2 files changed

+42
-40
lines changed

2 files changed

+42
-40
lines changed

Sources/SwiftFormatConfiguration/Configuration.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,12 @@ public struct Configuration: Codable, Equatable {
120120
self.version = highestSupportedConfigurationVersion
121121
}
122122

123+
/// Constructs a Configuration by loading it from a configuration file.
124+
public init(contentsOf url: URL) throws {
125+
let data = try Data(contentsOf: url)
126+
self = try JSONDecoder().decode(Configuration.self, from: data)
127+
}
128+
123129
public init(from decoder: Decoder) throws {
124130
let container = try decoder.container(keyedBy: CodingKeys.self)
125131

@@ -189,6 +195,27 @@ public struct Configuration: Codable, Equatable {
189195
forKey: .lineBreakAroundMultilineExpressionChainComponents)
190196
try container.encode(rules, forKey: .rules)
191197
}
198+
199+
/// Returns the URL of the configuration file that applies to the given file or directory.
200+
public static func url(forConfigurationFileApplyingTo url: URL) -> URL? {
201+
var path = url.absoluteURL
202+
let configFilename = ".swift-format"
203+
var isDirectory: ObjCBool = false
204+
if FileManager.default.fileExists(atPath: path.path, isDirectory: &isDirectory),
205+
isDirectory.boolValue {
206+
// will be deleted in a loop
207+
path.appendPathComponent("placeholder")
208+
}
209+
repeat {
210+
path.deleteLastPathComponent()
211+
let candidateFile = path.appendingPathComponent(configFilename)
212+
if FileManager.default.isReadableFile(atPath: candidateFile.path) {
213+
return candidateFile
214+
}
215+
} while path.path != "/"
216+
217+
return nil
218+
}
192219
}
193220

194221
/// Configuration for the NoPlaygroundLiterals rule.

Sources/swift-format/main.swift

Lines changed: 15 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -88,54 +88,29 @@ fileprivate func processSources(
8888
fileprivate func loadConfiguration(
8989
forSwiftFile swiftFilePath: String?, configFilePath: String?
9090
) -> Configuration {
91-
if let path = configFilePath {
92-
return decodedConfiguration(fromFileAtPath: path)
91+
if let configFilePath = configFilePath {
92+
return decodedConfiguration(fromFile: URL(fileURLWithPath: configFilePath))
9393
}
94-
// Search for a ".swift-format" configuration file in the directory of the current .swift file,
95-
// or its nearest parent.
96-
var configurationFilePath: String? = nil
97-
if let swiftFilePath = swiftFilePath {
98-
configurationFilePath = findConfigurationFile(
99-
forSwiftFile: URL(fileURLWithPath: swiftFilePath).path)
100-
}
101-
return decodedConfiguration(fromFileAtPath: configurationFilePath)
102-
}
10394

104-
/// Look for a ".swift-format" configuration file in the same directory as "forSwiftFile", or its
105-
/// nearest parent. If one is not found, return "nil".
106-
fileprivate func findConfigurationFile(forSwiftFile: String) -> String? {
107-
let cwd = FileManager.default.currentDirectoryPath
108-
var path = URL(
109-
fileURLWithPath: AbsolutePath(forSwiftFile, relativeTo: AbsolutePath(cwd)).pathString)
110-
let configFilename = ".swift-format"
111-
112-
repeat {
113-
path = path.deletingLastPathComponent()
114-
let testPath = path.appendingPathComponent(configFilename).path
115-
if FileManager.default.isReadableFile(atPath: testPath) {
116-
return testPath
117-
}
118-
} while path.path != "/"
95+
if let swiftFileURL = swiftFilePath.map(URL.init(fileURLWithPath:)),
96+
let configFileURL = Configuration.url(forConfigurationFileApplyingTo: swiftFileURL) {
97+
return decodedConfiguration(fromFile: configFileURL)
98+
}
11999

120-
return nil
100+
return Configuration()
121101
}
122102

103+
123104
/// Loads and returns a `Configuration` from the given JSON file if it is found and is valid. If the
124105
/// file does not exist or there was an error decoding it, the program exits with a non-zero exit
125106
/// code.
126-
fileprivate func decodedConfiguration(fromFileAtPath path: String?) -> Configuration {
127-
if let path = path {
128-
do {
129-
let url = URL(fileURLWithPath: path)
130-
let data = try Data(contentsOf: url)
131-
return try JSONDecoder().decode(Configuration.self, from: data)
132-
} catch {
133-
// TODO: Improve error message, write to stderr.
134-
print("Could not load configuration at \(path): \(error)")
135-
exit(1)
136-
}
137-
} else {
138-
return Configuration()
107+
fileprivate func decodedConfiguration(fromFile url: Foundation.URL) -> Configuration {
108+
do {
109+
return try Configuration(contentsOf: url)
110+
} catch {
111+
// TODO: Improve error message, write to stderr.
112+
print("Could not load configuration at \(url): \(error)")
113+
exit(1)
139114
}
140115
}
141116

0 commit comments

Comments
 (0)