Add --stdinpath option

This commit is contained in:
Nick Lockwood
2020-07-07 22:13:36 +01:00
parent 0e511cceeb
commit 82dcb2f48d
4 changed files with 82 additions and 52 deletions
+2 -2
View File
@@ -326,11 +326,11 @@ Git pre-commit hook
3. Edit or create a `.git/hooks/pre-commit` file in your project folder. The .git folder is hidden but should already exist if you are using Git with your project, so open it with the terminal, or the Finder's `Go > Go to Folder...` menu.
4. Add the following line in the pre-commit file:
4. Add the following line in the pre-commit file. The `{}` will be replaced automatically by the path to the Swift file being formatted:
```bash
#!/bin/bash
git-format-staged --formatter "swiftformat --config .swiftformat stdin" "*.swift"
git-format-staged --formatter "swiftformat stdin --stdinpath {}" "*.swift"
```
(Note that this example uses your locally installed version of SwiftFormat, not a separate copy in your project repository. You can replace `swiftformat` with the path to a copy inside your project if you prefer.)
+1
View File
@@ -514,6 +514,7 @@ let optionsArguments = fileArguments + rulesArguments + formattingArguments + in
let commandLineArguments = [
// Input options
"filelist",
"stdinpath",
"config",
"inferoptions",
"output",
+34 -5
View File
@@ -176,6 +176,7 @@ func printHelp(as type: CLI.OutputType) {
<file> <file> ... Swift files or directories to be processed, or "stdin"
--filelist Path to a file with names of files to process, one per line
--stdinpath Path to stdin source file (used for generating header)
--config Path to a configuration file containing rules and options
--inferoptions Instead of formatting input, use it to infer format options
--output Output path for formatted file(s) (defaults to input path)
@@ -358,7 +359,7 @@ func processArguments(_ args: [String], in directory: String) -> ExitCode {
}
// Options
let options = try Options(args, in: directory)
var options = try Options(args, in: directory)
// Show rules
if showRules {
@@ -386,7 +387,6 @@ func processArguments(_ args: [String], in directory: String) -> ExitCode {
}
// Input path(s)
var useStdin = false
var inputURLs = [URL]()
if let fileListPath = args["filelist"] {
let fileListURL = try parsePath(fileListPath, for: "filelist", in: directory)
@@ -397,13 +397,38 @@ func processArguments(_ args: [String], in directory: String) -> ExitCode {
throw FormatError.options("Failed to read file list at \(fileListPath)")
}
}
while !useStdin, let inputPath = args[String(inputURLs.count + 1)] {
var useStdin = false
while let inputPath = args[String(inputURLs.count + 1)] {
inputURLs += try parsePaths(inputPath, for: "input", in: directory)
if inputPath.lowercased() == "stdin" {
useStdin = true
} else {
inputURLs += try parsePaths(inputPath, for: "input", in: directory)
}
}
if useStdin {
if inputURLs.count > 1 {
if args["filelist"] != nil {
throw FormatError.options("--filelist option cannot be combined with stdin input")
}
throw FormatError.options("Cannot combine stdin with other file inputs")
}
inputURLs = []
}
if let stdinPath = args["stdinpath"] {
if !useStdin {
print("warning: --stdinpath option only applies when using stdin", as: .warning)
}
let stdinURL = try parsePath(stdinPath, for: "stdinpath", in: directory)
let resourceValues = try getResourceValues(
for: stdinURL.standardizedFileURL,
keys: [.creationDateKey, .pathKey]
)
var formatOptions = options.formatOptions ?? .default
formatOptions.fileInfo = FileInfo(
filePath: resourceValues.path,
creationDate: resourceValues.creationDate
)
options.formatOptions = formatOptions
}
// Treat values for arguments that do not take a value as input paths
func addInputPaths(for argName: String) throws {
@@ -720,6 +745,10 @@ func applyRules(_ source: String, options: Options, verbose: Bool, lint: Bool) t
let ruleNames = Array(options.rules ?? allRules.subtracting(FormatRules.disabledByDefault)).sorted()
let rules = ruleNames.compactMap { rulesByName[$0] }
if verbose, let path = options.formatOptions?.fileInfo.filePath {
print("\(lint ? "Linting" : "Formatting") \(path)", as: .info)
}
// Apply rules
let formatOptions = options.formatOptions ?? .default
var changes = [Formatter.Change]()
+45 -45
View File
@@ -100,47 +100,6 @@ public func enumerateFiles(withInputURL inputURL: URL,
.creationDateKey, .pathKey,
]
struct ResourceValues {
let isRegularFile: Bool?
let isDirectory: Bool?
let isAliasFile: Bool?
let isSymbolicLink: Bool?
let creationDate: Date?
let path: String?
}
func getResourceValues(for url: URL) throws -> ResourceValues {
#if os(macOS)
if let resourceValues = try? url.resourceValues(forKeys: Set(keys)) {
return ResourceValues(
isRegularFile: resourceValues.isRegularFile,
isDirectory: resourceValues.isDirectory,
isAliasFile: resourceValues.isAliasFile,
isSymbolicLink: resourceValues.isSymbolicLink,
creationDate: resourceValues.creationDate,
path: resourceValues.path
)
}
if manager.fileExists(atPath: url.path) {
throw FormatError.reading("Failed to read attributes for \(url.path)")
}
throw FormatError.options("File not found at \(url.path)")
#else
var isDirectory: ObjCBool = false
if manager.fileExists(atPath: url.path, isDirectory: &isDirectory) {
return ResourceValues(
isRegularFile: !isDirectory.boolValue,
isDirectory: isDirectory.boolValue,
isAliasFile: false,
isSymbolicLink: false,
creationDate: nil,
path: url.path
)
}
throw FormatError.options("File not found at \(url.path)")
#endif
}
let group = DispatchGroup()
var completionBlocks = [() throws -> Void]()
let completionQueue = DispatchQueue(label: "swiftformat.enumeration")
@@ -156,12 +115,12 @@ public func enumerateFiles(withInputURL inputURL: URL,
let fileOptions = options.fileOptions ?? .default
let inputURL = inputURL.standardizedFileURL
do {
let resourceValues = try getResourceValues(for: inputURL)
let resourceValues = try getResourceValues(for: inputURL, keys: keys)
if resourceValues.isAliasFile == true {
#if os(macOS)
if fileOptions.followSymlinks {
let resolvedURL = try URL(resolvingAliasFileAt: inputURL)
return (resolvedURL, try getResourceValues(for: resolvedURL), baseOptions)
return (resolvedURL, try getResourceValues(for: resolvedURL, keys: keys), baseOptions)
} else if let handler = skipped {
onComplete(try handler(inputURL, inputURL, options))
return nil
@@ -170,7 +129,7 @@ public func enumerateFiles(withInputURL inputURL: URL,
} else if resourceValues.isSymbolicLink == true {
if fileOptions.followSymlinks {
let resolvedURL = inputURL.resolvingSymlinksInPath()
return (resolvedURL, try getResourceValues(for: resolvedURL), baseOptions)
return (resolvedURL, try getResourceValues(for: resolvedURL, keys: keys), baseOptions)
} else if let handler = skipped {
onComplete(try handler(inputURL, inputURL, options))
return nil
@@ -185,7 +144,7 @@ public func enumerateFiles(withInputURL inputURL: URL,
let fileOptions = baseOptions.fileOptions ?? .default
do {
let resourceValues = try getResourceValues(for: inputURL.standardizedFileURL)
let resourceValues = try getResourceValues(for: inputURL.standardizedFileURL, keys: keys)
if !fileOptions.followSymlinks,
resourceValues.isAliasFile == true || resourceValues.isSymbolicLink == true {
return [FormatError.options("Symbolic link or alias was skipped: \(inputURL.path)")]
@@ -612,6 +571,47 @@ public func expandPath(_ path: String, in directory: String) -> URL {
return URL(fileURLWithPath: directory).appendingPathComponent(path)
}
struct ResourceValues {
let isRegularFile: Bool?
let isDirectory: Bool?
let isAliasFile: Bool?
let isSymbolicLink: Bool?
let creationDate: Date?
let path: String?
}
func getResourceValues(for url: URL, keys: [URLResourceKey]) throws -> ResourceValues {
let manager = FileManager.default
#if os(macOS)
if let resourceValues = try? url.resourceValues(forKeys: Set(keys)) {
return ResourceValues(
isRegularFile: resourceValues.isRegularFile,
isDirectory: resourceValues.isDirectory,
isAliasFile: resourceValues.isAliasFile,
isSymbolicLink: resourceValues.isSymbolicLink,
creationDate: resourceValues.creationDate,
path: resourceValues.path
)
}
if manager.fileExists(atPath: url.path) {
throw FormatError.reading("Failed to read attributes for \(url.path)")
}
#else
var isDirectory: ObjCBool = false
if manager.fileExists(atPath: url.path, isDirectory: &isDirectory) {
return ResourceValues(
isRegularFile: !isDirectory.boolValue,
isDirectory: isDirectory.boolValue,
isAliasFile: false,
isSymbolicLink: false,
creationDate: nil,
path: url.path
)
}
#endif
throw FormatError.options("File not found at \(url.path)")
}
// MARK: Documentation utilities
// Strip markdown code-formatting