mirror of
https://github.com/nicklockwood/SwiftFormat.git
synced 2026-05-17 10:30:35 +00:00
114 lines
4.1 KiB
Swift
114 lines
4.1 KiB
Swift
//
|
|
// IsEmpty.swift
|
|
// SwiftFormat
|
|
//
|
|
// Created by Nick Lockwood on 12/15/18.
|
|
// Copyright © 2024 Nick Lockwood. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
public extension FormatRule {
|
|
/// Replace count == 0 with isEmpty
|
|
static let isEmpty = FormatRule(
|
|
help: "Prefer `isEmpty` over comparing `count` against zero.",
|
|
disabledByDefault: true
|
|
) { formatter in
|
|
formatter.forEach(.identifier("count")) { i, _ in
|
|
guard let dotIndex = formatter.index(of: .nonSpaceOrLinebreak, before: i, if: {
|
|
$0.isOperator(".")
|
|
}), let opIndex = formatter.index(of: .nonSpaceOrLinebreak, after: i, if: {
|
|
$0.isOperator
|
|
}), let endIndex = formatter.index(of: .nonSpaceOrLinebreak, after: opIndex, if: {
|
|
$0 == .number("0", .integer)
|
|
}) else {
|
|
return
|
|
}
|
|
var isOptional = false
|
|
var index = dotIndex
|
|
var wasIdentifier = false
|
|
loop: while true {
|
|
guard let prev = formatter.index(of: .nonSpaceOrCommentOrLinebreak, before: index) else {
|
|
break
|
|
}
|
|
switch formatter.tokens[prev] {
|
|
case .operator("!", _), .operator(".", _):
|
|
break // Ignored
|
|
case .operator("?", _):
|
|
if formatter.tokens[prev - 1].isSpace {
|
|
break loop
|
|
}
|
|
isOptional = true
|
|
case let .operator(op, .infix):
|
|
guard ["||", "&&", ":"].contains(op) else {
|
|
return
|
|
}
|
|
break loop
|
|
case .keyword, .delimiter, .startOfScope:
|
|
break loop
|
|
case .identifier:
|
|
if wasIdentifier {
|
|
break loop
|
|
}
|
|
wasIdentifier = true
|
|
index = prev
|
|
continue
|
|
case .endOfScope:
|
|
guard !wasIdentifier, let start = formatter.index(of: .startOfScope, before: prev) else {
|
|
break loop
|
|
}
|
|
wasIdentifier = false
|
|
index = start
|
|
continue
|
|
default:
|
|
break
|
|
}
|
|
wasIdentifier = false
|
|
index = prev
|
|
}
|
|
let isEmpty: Bool
|
|
switch formatter.tokens[opIndex] {
|
|
case .operator("==", .infix): isEmpty = true
|
|
case .operator("!=", .infix), .operator(">", .infix): isEmpty = false
|
|
default: return
|
|
}
|
|
if isEmpty {
|
|
if isOptional {
|
|
formatter.replaceTokens(in: i ... endIndex, with: [
|
|
.identifier("isEmpty"), .space(" "), .operator("==", .infix), .space(" "), .identifier("true"),
|
|
])
|
|
} else {
|
|
formatter.replaceTokens(in: i ... endIndex, with: .identifier("isEmpty"))
|
|
}
|
|
} else {
|
|
if isOptional {
|
|
formatter.replaceTokens(in: i ... endIndex, with: [
|
|
.identifier("isEmpty"), .space(" "), .operator("!=", .infix), .space(" "), .identifier("true"),
|
|
])
|
|
} else {
|
|
formatter.replaceTokens(in: i ... endIndex, with: .identifier("isEmpty"))
|
|
formatter.insert(.operator("!", .prefix), at: index)
|
|
}
|
|
}
|
|
}
|
|
} examples: {
|
|
"""
|
|
```diff
|
|
- if foo.count == 0 {
|
|
+ if foo.isEmpty {
|
|
|
|
- if foo.count > 0 {
|
|
+ if !foo.isEmpty {
|
|
|
|
- if foo?.count == 0 {
|
|
+ if foo?.isEmpty == true {
|
|
```
|
|
|
|
***NOTE:*** In rare cases, the `isEmpty` rule may insert an `isEmpty` call for
|
|
a type that doesn't implement that property, breaking the program. For this
|
|
reason, the rule is disabled by default, and must be manually enabled via the
|
|
`--enable isEmpty` option.
|
|
"""
|
|
}
|
|
}
|