Skip to content

Commit 337aad4

Browse files
committed
Filter urls to remove ignored ones.
1 parent 549fafe commit 337aad4

File tree

1 file changed

+39
-5
lines changed

1 file changed

+39
-5
lines changed

Sources/SwiftFormat/Utilities/FileIterator.swift

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ import Foundation
1717
@_spi(Internal)
1818
public struct FileIterator: Sequence, IteratorProtocol {
1919

20-
/// Name of the suppression file to look for.
20+
/// Name of the ignore file to look for.
2121
/// The presence of this file in a directory will cause the formatter
2222
/// to skip formatting files in that directory and its subdirectories.
23-
private static let suppressionFileName = ".swift-format-ignore"
23+
fileprivate static let ignoreFileName = ".swift-format-ignore"
2424

2525
/// List of file and directory URLs to iterate over.
2626
private let urls: [URL]
@@ -53,7 +53,7 @@ public struct FileIterator: Sequence, IteratorProtocol {
5353
/// The given URLs may be files or directories. If they are directories, the iterator will recurse
5454
/// into them.
5555
public init(urls: [URL], followSymlinks: Bool) {
56-
self.urls = urls
56+
self.urls = urls.filter(inputShouldBeProcessed(at:))
5757
self.urlIterator = self.urls.makeIterator()
5858
self.followSymlinks = followSymlinks
5959
}
@@ -88,8 +88,8 @@ public struct FileIterator: Sequence, IteratorProtocol {
8888
fallthrough
8989

9090
case .typeDirectory:
91-
let suppressionFile = next.appendingPathComponent(Self.suppressionFileName)
92-
if FileManager.default.fileExists(atPath: suppressionFile.path) {
91+
let ignoreFile = next.appendingPathComponent(Self.ignoreFileName)
92+
if FileManager.default.fileExists(atPath: ignoreFile.path) {
9393
continue
9494
}
9595

@@ -179,3 +179,37 @@ private func fileType(at url: URL) -> FileAttributeType? {
179179
// Linux.
180180
return try? FileManager.default.attributesOfItem(atPath: url.path)[.type] as? FileAttributeType
181181
}
182+
183+
/// Returns true if the file should be processed.
184+
/// Directories are always processed.
185+
/// Other files are processed if there is not a
186+
/// ignore file in the containing directory or any of its parents.
187+
private func inputShouldBeProcessed(at url: URL) -> Bool {
188+
guard fileType(at: url) != .typeDirectory else {
189+
return true
190+
}
191+
192+
var containingDirectory = url.absoluteURL.standardized.deletingLastPathComponent()
193+
repeat {
194+
containingDirectory.deleteLastPathComponent()
195+
let candidateFile = containingDirectory.appendingPathComponent(FileIterator.ignoreFileName)
196+
if FileManager.default.isReadableFile(atPath: candidateFile.path) {
197+
return false
198+
}
199+
} while !containingDirectory.isRoot
200+
return true
201+
}
202+
203+
fileprivate extension URL {
204+
var isRoot: Bool {
205+
#if os(Windows)
206+
// FIXME: We should call into Windows' native check to check if this path is a root once https://github.com/swiftlang/swift-foundation/issues/976 is fixed.
207+
// https://github.com/swiftlang/swift-format/issues/844
208+
return self.pathComponents.count <= 1
209+
#else
210+
// On Linux, we may end up with an string for the path due to https://github.com/swiftlang/swift-foundation/issues/980
211+
// TODO: Remove the check for "" once https://github.com/swiftlang/swift-foundation/issues/980 is fixed.
212+
return self.path == "/" || self.path == ""
213+
#endif
214+
}
215+
}

0 commit comments

Comments
 (0)