nicklockwood / swiftformat Goto Github PK
View Code? Open in Web Editor NEWA command-line tool and Xcode Extension for formatting Swift code
License: MIT License
A command-line tool and Xcode Extension for formatting Swift code
License: MIT License
Before:
if let (_, v) = e.fields
After:
if let(_, v) = e.fields
Notice missing space after let
I noticed that SwiftFormat doesn't format double wrapped optionals that use dot notation correctly. It takes the working code and changes it so it can't compile. Here's an example of working code:
let hello: String?? = "Hello"
let capitalHello = hello??.capitalizedString
After SwiftFormat:
let hello: String?? = "Hello"
let capitalHello = hello ??. capitalizedString
Notice the extra space before and after ??.
in the second line. This is reproducible 100% of the time for me.
Hi, great tool!
One issue I've come across is with optional function parameters that default to nil
value:String?=nil
gets changed to
value: String ?= nil
which results in a compile time error. It should be
value: String? = nil
I would like to see colon alignment for method parameters like this:
let result = self.method(param1: value1,
param2: value2,
param3333: value3333)
I'm not sure if this is the swift way, but I think it would make the code look cleaner like in Obj-C
Given a trivial documentation comment for a method, its documentation is rendered as follows
When swiftformat is run, the following is produced
I believe this to be a result of adding trailing white space to the first /**
and then the closing */
is preceded by whitespace. This turns it into a code block.
Hi Nick!
First of all great work on this tool, the code wasn't that hard to read and it was well documented, the installation steps were top notch, getting this running was easy peasy.
Ok, now to the question, I've read on Twitter that you were sticking to Apple's style/guidelines when it comes to formatting, but I've noticed that you're adding a newline bellow struct and class implementations meanwhile in the Apple's Swift book doesn't do this. What was your reasoning behind this change?
Hi Nick,
switch x {
case a:
try FileManager.default.removeItem(atPath: z)
default:
break
}
fails to be parsed (v0.9.3). It is confused by the default
of Filemanager
(as Github syntax highlighting!).
For example, the following code
private var buffer: Array<T?>
buffer = Array<T?>(count: capacity, repeatedValue: nil)
is converted to
private var buffer: Array < T?>
buffer = Array < T ?> (count: capacity, repeatedValue: nil)
as the ?> is treated as an operator
I just read your blog posts about SwiftFormat. Great writeup!
In part 3, I was surprised to read about the hoops you had to jump through to preprocess command-line arguments to deal with filenames with spaces or escaped quotes. The shell should handle this for you, shouldn't it?
Consider this tiny test program that simply prints the arguments it received:
// cli.swift
print("argc: \(CommandLine.argc)")
print("arguments: \(CommandLine.arguments)")
This handles everything I threw at it in a short test correctly because the shell already passes the correct array to the process:
$ swiftc cli.swift
$ ./cli someFile.txt
argc: 2
arguments: ["./cli", "someFile.txt"]
$ ./cli file\ with\ spaces.txt
argc: 2
arguments: ["./cli", "file with spaces.txt"]
$ ./cli "file with spaces.txt"
argc: 2
arguments: ["./cli", "file with spaces.txt"]
$ ./cli "file \"with\" quotes.txt"
argc: 2
arguments: ["./cli", "file \"with\" quotes.txt"]
# If you omit the quotes, you get separate strings (as expected)
$ ./cli file with spaces.txt
argc: 4
arguments: ["./cli", "file", "with", "spaces.txt"]
I think it's possible the behavior is different if you start the process differently (i.e. not from a shell), but I'm not sure. I'd be interested in hearing how you tested this, i.e. in what situation did you encounter the behavior you describe in the blog post?
Thanks!
Or must build the rules by myself?
if a comment after a line, SwiftFormat will add one more line before function, so there are two blank lines
public var items: [[BaseItem]] = [[]] // default menu items
public final override func onPerform<T: Item>(action: Action, item: T) {
Closures aren't being correctly indented, as shown in the examples below. This is happening whether there are parameters in the closure or not.
for validatableResult in validatableResults {
validatableResult
.filter { (_, field) in field.isFocused }
.observeIn(DispatchQueue.main.context)
.observeNext { [unowned self] (result, field) in
if result.isNotValid {
self.expressionResultPopoverController.showResult(for: result, attachedTo: field)
} else {
self.expressionResultPopoverController.performClose()
}
}
.disposeIn(disposeBag)
}
for validatableResult in validatableResults {
validatableResult
.filter { (_, field) in field.isFocused }
.observeIn(DispatchQueue.main.context)
.observeNext { [unowned self] (result, field) in
if result.isNotValid {
self.expressionResultPopoverController.showResult(for: result, attachedTo: field)
} else {
self.expressionResultPopoverController.performClose()
}
}
.disposeIn(disposeBag)
}
Currently SwiftFormat
is removing space between @escaping
and closure type, so instead of @escaping (String) -> Void
it end up as @escaping(String) -> Void
. Is it intentional behaviour? For me it looks strange - like @escaping
only applies to arguments of the closure, instead of whole closure type. Of course it doesn't make sense for someone who knows Swift, but still - formatting, at the first sight, might be confusing.
Consider this code that follows a "always braces in new line" style with a comment following an if condition-
var networkEnabled = true
if networkEnabled //We want to enable the UI..
{
}
The tool ends up converting the above code to-
var networkEnabled = true
if networkEnabled //We want to enable the UI.. {
}
This will result in compiler error as obviously the opening brace after the if is effectively commented out.
Array<Array<Dictionary<String, Int>>>
turns in to:
Array < Array < Dictionary < String, Int>>>
It’s been deprecated in Swift 3, but valid Swift 2.3 attribute @autoclosure(escaping)
gets reformatted as @autoclosure (escaping)
Hi again Nick
This is related to #28: when a line ends with default
, the next non empty line is indented, and if this line is a comment, the line after the comment is indented too (v0.9.4).
For example
let f = FileManager.default
// Comment
let x = 0
is formatted as
let f = FileManager.default
// Comment
let x = 0
Appending a semicolon after default
prevents this.
I have below code
let users: MultipleModels = ....
After run swfitformat command the code comes to be
let users: MultipleModels < User> = ...
it looks like there is whitespace was added after 'MultipleModels' and before 'User' type.
i.e. seems generics symbol '<' was treated as 'Less Than'.
This might be a problem, can we fix it?
btw: SwiftFormat is awesome for me to use in our project. Compared with other swift format tools, this one is pretty good, and the formatted result is acceptable. Thanks for your contribution.
Xcode indent:
let result = self.method(value1,
param2: value2,
param3: value3)
SwiftFormat:
let result = self.method(value1,
param2: value2,
param3: value3)
Is there a setting to keep the format matching Xcode for this case?
Hi Nick,
SwiftFormat (v0.9.5) fails to parse the file when it contains something like:
struct A {
func f(x: Bool) -> Bool {
switch x {
case true: return false
default: return true
}
}
}
enum B {
case foo
}
It seems confused with the case
's in f
and those of B
.
Great start on what could be a very useful tool.
What would be great is if you could work on a configurable set of flags to turn on/off.
Examples
Great start!!
Multiple newlines at the end of a swift file is not replaced by a single newline. Observed using version 0.7.1 and I am unaware if this is also the case when using previous versions.
Add extra line after {
and before }
only for brackets without any indentation.
It is common to write code like this:
class Foo {
func bar() {
// Do something here
}
}
or:
enum Foo {
case a
case b
case c
}
We have a debug feature to crash the application and we use this code:
let crash: String? = nil; let _ = crash!
The "String? = nil" was reformatted as "String ?= nil" which does not compile
init?<T: UIViewController where T: protocol<WZStatusCallback, WZAudioSink, WZVideoSink>>(withViewController viewController: T) {
// Initialization Code
}
In this case (Failable Initialization), SwiftFormat will make
?
and you will get compile error. (strict by syntax)Sample code:
let a = (true ? 1 : 0) |
(true ? 1 : 0) |
(true ? 1 : 0) |
(true ? 1 : 0)
let b = (true ? 1 : 0) |
// (true ? 1 : 0) |
(true ? 1 : 0) |
(true ? 1 : 0)
let c = [1,
2,
// 3,
4,
]
Result:
let a = (true ? 1 : 0) |
(true ? 1 : 0) |
(true ? 1 : 0) |
(true ? 1 : 0)
let b = (true ? 1 : 0) |
// (true ? 1 : 0) |
(true ? 1 : 0) |
(true ? 1 : 0)
let c = [1,
2,
// 3,
4,
]
Seems to be working fine multiline literals
Sorry dude, I'm opening new issues as not to flood the original. We're like 95% of the way there for my use case 😁
Given:
@IBAction func addPaddingToDocument(_ sender: AnyObject?) {
if let button = sender as? NSButton {
defer {
// Do a deferred thing
}
}
viewModel.crop()
.observe { [unowned self] event in
switch event {
case .completed:
// Do a thing
break
case .failed(let error):
// Do another thing
break
default:
// Do a default thing.
break
}
}
.disposeIn(self.bnd_bag)
}
I would expect similar formatting to the sample above (we can argue about the switch indentation some other time), however SwiftFormat 0.11.2 is outputting the following:
@IBAction func addPaddingToDocument(_ sender: AnyObject?) {
if let button = sender as? NSButton {
defer {
// Do a deferred thing
}
}
viewModel.crop()
.observe { [unowned self] event in
switch event {
case .completed:
// Do a thing
break
case .failed(let error):
// Do another thing
break
default:
// Do a default thing.
break
}
}
.disposeIn(self.bnd_bag)
}
Here is a sample document: closure.swift.zip. Removing the if let
from the top of the method makes SwiftFormat output the expected indentation for the closure.
${SRCROOT}/path/to/swiftformat -f /path/to/your/swift/code/
Results in a: error: unknown argument: -f.
Shouldn't the -f be removed? I wasn't sure if there was a "hidden" argument for a build environment..?
I removed the -f, and it seems to be functioning as intended.
I was hoping for a mechanism to customize or disable the rules, outside of building a custom binary.
Currently I remove the blankLinesAtEndOfScope
rule, because I like to end structs/classes/enums with a blank line, and make sure manually to not do the same for functions. If that was customizable, that would be great, otherwise I'd like to be able to just disable the rule.
This tool is great though, thanks a lot!
Given the following closure:
observeNext { [unowned self] (result, textField) in
// Do the thing with the thing
}
I would expect the space to remain between [unowned self]
and the parameters (result, textField)
, however SwiftFormat 0.11.2 is removing the space.
We just got SwiftFormat running for our team and noticed that it failed with the following error for any projects that were included in a folder with a space in the name. One example is: Documents/Xcode Projects/ProjectToRunSwiftLintOn/
The error we saw in Xcode's build log:
/Users/danielgalasko/Library/Developer/Xcode/DerivedData/OurApp-dznbqhvktztcjrfezxvdxareyphq/Build/Intermediates/OurApp.build/Debug-iphonesimulator/OurAppKit.build/Script-E634E70E1D79C26D005A6B25.sh: line 2: /Users/danielgalasko/Documents/Xcode: No such file or directory
Changing the directory name to XcodeProjects
fixed the issue
Thanks for the awesome tool! 👍
Given the following before formatting:
func ==<T>(left: T, right: T) -> Bool {
return true
}
I get this after formatting:
func ==<T > (left: T, right: T) -> Bool {
return true
}
I'd love to see a FormatOption
property to treat the passed input as a fragment of a larger section of code. This would aid with formatting user selections in Xcode, rather than the entire file.
I believe the "simplest" way to implement this would be to take the existing indent of the first line of code passed to SwiftFormat.format()
and retain it (and indent subsequent lines relative to the first line).
This one might require bike shedding. I'm not 100% sure what the general convention is for this, but I prefer to split my guard statements up over a number of lines.
Current SwiftFormat will do this:
guard
let nextFocusedIndexPath = context.nextFocusedIndexPath,
nextFocusedIndexPath != context.previouslyFocusedIndexPath
else {
return
}
Whereas I would prefer that it outdent the else
to align with the guard
start (much like an if…else
control statement):
guard
let nextFocusedIndexPath = context.nextFocusedIndexPath,
nextFocusedIndexPath != context.previouslyFocusedIndexPath
else {
return
}
In my eyes, that's easier to read, but I understand that's a personal preference.
Right now, if you have:
class MyClass {
func myFunc(){ }
EMPTY LINE
func other() {}
}
EMPTY LINE won't be indented, keeping them indented is Xcode's default and practical, in my opinion, since you don't have to indent when you want to use that line.
I don't suggest that the default behavior is changed, but I'd like to make it an option. I tried to modify the indent
rule to allow that but was not successful, I would appreciate some guidance.
(Void) throws -> Void
turns in to
Void throws -> Void
without the throws
it correctly turns in to
() -> Void
0...i
0..<i
Before:
completion(resource.transform(responseData).mapError { .Other(request: request, error: $0) })
After:
completion(resource.transform(responseData).mapError {.Other(request: request, error: $0) })
Space before .Other
is missing
was if #available(iOS 9.0, *) {
// autosuggested and generated by Xcode when using unavailable APIs
swiftformat 0.16.4: if #available (iOS 9.0, *) {
With v0.9.1
switch y {
case x is [Key: Value]:
fallthrough
default: break
}
fails to be parsed. It seems to be caused by the colon after Key
.
For this configuration, a comma is appended after each run of SwiftFormat (v0.9):
let A = [
x, // Some comment
]
becomes after a few runs:
let A = [
x, // Some comment,,,
]
If the array have several rows, it only affects the last one.
When having more than two callbacks, indentation just lost, it always consider they are at same level.
Code Before:
self.view.startSpinning()
weak var weakSelf = self
potentialHubViewModel.getCorridorInfo().subscribeNext({
response in
if( weakSelf == nil ){
return
}
weakSelf?.view.stopSpinning()
if( response == nil ){
LOG.ELog("[Error] getCorridorFullInfo failed with nil response!")
return
}
let apiResponse = response as! ApiResponseJSON
if apiResponse.status != 200 {
LOG.ELog("failed with error code: \(apiResponse.errorCode) and status: \(apiResponse.status)")
}
},
error: {
(error: NSError!) -> Void in
LOG.ELog("failed with error: \(error)")
if( weakSelf == nil ){
return
}
weakSelf?.view.stopSpinning()
})
Code After:
self.view.startSpinning()
weak var weakSelf = self
potentialHubViewModel.getCorridorInfo().subscribeNext({
response in
if (weakSelf == nil) {
return
}
weakSelf?.view.stopSpinning()
if (response == nil) {
LOG.ELog("[Error] getCorridorFullInfo failed with nil response!")
return
}
let apiResponse = response as! ApiResponseJSON
if apiResponse.status != 200 {
LOG.ELog("failed with error code: \(apiResponse.errorCode) and status: \(apiResponse.status)")
}
},
error: {
(error: NSError!) -> Void in
LOG.ELog("failed with error: \(error)")
if (weakSelf == nil) {
return
}
weakSelf?.view.stopSpinning()
})
Given the following bit of code (note the trailing newline):
func doSomething() {
// Hello
}
I would expect the output to be the same as the input here, however SwiftFormat is removing that trailing newline. If there were two trailing newlines, then yeah, I reckon trim one of them - but not a single trailing newline.
This is affecting #39, as a common use-case is to select a function in amongst other functions and format it. I started to add a workaround, but I think this is actually something worth solving in the library.
Thanks!
- @objc(sg_currencyStringFromNumber:currency:)
+ @objc(sg_currencyStringFromNumber: currency:)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.