1+ //===----------------------------------------------------------------------===//
2+ //
3+ // This source file is part of the Swift.org open source project
4+ //
5+ // Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
6+ // Licensed under Apache License v2.0 with Runtime Library Exception
7+ //
8+ // See https://swift.org/LICENSE.txt for license information
9+ // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+ //
11+ //===----------------------------------------------------------------------===//
12+
13+ import Foundation
14+
15+ public class IgnoreFile {
16+ /// Name of the ignore file to look for.
17+ /// The presence of this file in a directory will cause the formatter
18+ /// to skip formatting files in that directory and its subdirectories.
19+ fileprivate static let fileName = " .swift-format-ignore "
20+
21+ /// Errors that can be thrown by the IgnoreFile initializer.
22+ public enum Error : Swift . Error {
23+ /// Error thrown when an ignore file has invalid content.
24+ case invalidContent( URL )
25+ }
26+
27+ /// Create an instance from the contents of the file at the given URL.
28+ /// Throws an error if the file content can't be read, or is not valid.
29+ public init ( contentsOf url: URL ) throws {
30+ let content = try String ( contentsOf: url, encoding: . utf8)
31+ guard content. trimmingCharacters ( in: . whitespacesAndNewlines) == " * " else {
32+ throw Error . invalidContent ( url)
33+ }
34+ }
35+
36+ /// Create an instance for the given directory, if a valid
37+ /// ignore file with the standard name is found in that directory.
38+ /// Returns nil if no ignore file is found.
39+ /// Throws an error if an invalid ignore file is found.
40+ ///
41+ /// Note that this initializer does not search parent directories for ignore files.
42+ public convenience init ? ( forDirectory directory: URL ) throws {
43+ let url = directory. appendingPathComponent ( IgnoreFile . fileName)
44+ guard FileManager . default. isReadableFile ( atPath: url. path) else {
45+ return nil
46+ }
47+
48+ try self . init ( contentsOf: url)
49+ }
50+
51+ /// Create an instance to use for the given URL.
52+ /// We search for an ignore file starting from the given URL's container,
53+ /// and moving up the directory tree, until we reach the root directory.
54+ /// Returns nil if no ignore file is found.
55+ /// Throws an error if an invalid ignore file is found somewhere
56+ /// in the directory tree.
57+ public convenience init ? ( for url: URL ) throws {
58+ var containingDirectory = url. absoluteURL. standardized
59+ repeat {
60+ containingDirectory. deleteLastPathComponent ( )
61+ let url = containingDirectory. appendingPathComponent ( IgnoreFile . fileName)
62+ if FileManager . default. isReadableFile ( atPath: url. path) {
63+ try self . init ( contentsOf: url)
64+ return
65+ }
66+ } while !containingDirectory. isRoot
67+ return nil
68+ }
69+
70+ /// Should the given URL be processed?
71+ /// Currently the only valid ignore file content is "*",
72+ /// which means that all files should be ignored.
73+ func shouldProcess( _ url: URL ) -> Bool {
74+ return false
75+ }
76+
77+ /// Returns true if the name of the given URL matches
78+ /// the standard ignore file name.
79+ public static func isStandardIgnoreFile( _ url: URL ) -> Bool {
80+ return url. lastPathComponent == fileName
81+ }
82+ }
0 commit comments