mirror of
https://github.com/nicklockwood/SwiftFormat.git
synced 2026-05-17 10:30:35 +00:00
121 lines
4.3 KiB
Swift
121 lines
4.3 KiB
Swift
//
|
|
// NumberFormatting.swift
|
|
// SwiftFormat
|
|
//
|
|
// Created by Nick Lockwood on 1/17/17.
|
|
// Copyright © 2024 Nick Lockwood. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
public extension FormatRule {
|
|
/// Standardize formatting of numeric literals
|
|
static let numberFormatting = FormatRule(
|
|
help: """
|
|
Use consistent grouping for numeric literals. Groups will be separated by `_`
|
|
delimiters to improve readability. For each numeric type you can specify a group
|
|
size (the number of digits in each group) and a threshold (the minimum number of
|
|
digits in a number before grouping is applied).
|
|
""",
|
|
options: ["decimal-grouping", "binary-grouping", "octal-grouping", "hex-grouping",
|
|
"fraction-grouping", "exponent-grouping", "hex-literal-case", "exponent-case"]
|
|
) { formatter in
|
|
formatter.forEachToken { i, token in
|
|
guard case let .number(number, type) = token else {
|
|
return
|
|
}
|
|
let grouping: Grouping
|
|
let prefix: String, exponentSeparator: String, parts: [String]
|
|
switch type {
|
|
case .integer, .decimal:
|
|
grouping = formatter.options.decimalGrouping
|
|
prefix = ""
|
|
exponentSeparator = formatter.options.uppercaseExponent ? "E" : "e"
|
|
parts = number.components(separatedBy: CharacterSet(charactersIn: ".eE"))
|
|
case .binary:
|
|
grouping = formatter.options.binaryGrouping
|
|
prefix = "0b"
|
|
exponentSeparator = ""
|
|
parts = [String(number[prefix.endIndex...])]
|
|
case .octal:
|
|
grouping = formatter.options.octalGrouping
|
|
prefix = "0o"
|
|
exponentSeparator = ""
|
|
parts = [String(number[prefix.endIndex...])]
|
|
case .hex:
|
|
grouping = formatter.options.hexGrouping
|
|
prefix = "0x"
|
|
exponentSeparator = formatter.options.uppercaseExponent ? "P" : "p"
|
|
parts = number[prefix.endIndex...].components(separatedBy: CharacterSet(charactersIn: ".pP")).map {
|
|
formatter.options.uppercaseHex ? $0.uppercased() : $0.lowercased()
|
|
}
|
|
}
|
|
var main = parts[0], fraction = "", exponent = ""
|
|
switch parts.count {
|
|
case 2 where number.contains("."):
|
|
fraction = parts[1]
|
|
case 2:
|
|
exponent = parts[1]
|
|
case 3:
|
|
fraction = parts[1]
|
|
exponent = parts[2]
|
|
default:
|
|
break
|
|
}
|
|
formatter.applyGrouping(grouping, to: &main)
|
|
if formatter.options.fractionGrouping {
|
|
formatter.applyGrouping(grouping, to: &fraction)
|
|
}
|
|
if formatter.options.exponentGrouping {
|
|
formatter.applyGrouping(grouping, to: &exponent)
|
|
}
|
|
var result = prefix + main
|
|
if !fraction.isEmpty {
|
|
result += "." + fraction
|
|
}
|
|
if !exponent.isEmpty {
|
|
result += exponentSeparator + exponent
|
|
}
|
|
formatter.replaceToken(at: i, with: .number(result, type))
|
|
}
|
|
} examples: {
|
|
"""
|
|
```diff
|
|
- let color = 0xFF77A5
|
|
+ let color = 0xff77a5
|
|
```
|
|
|
|
```diff
|
|
- let big = 123456.123
|
|
+ let big = 123_456.123
|
|
```
|
|
"""
|
|
}
|
|
}
|
|
|
|
extension Formatter {
|
|
func applyGrouping(_ grouping: Grouping, to number: inout String) {
|
|
switch grouping {
|
|
case .none, .group:
|
|
number = number.replacingOccurrences(of: "_", with: "")
|
|
case .ignore:
|
|
return
|
|
}
|
|
guard case let .group(group, threshold) = grouping, group > 0, number.count >= threshold else {
|
|
return
|
|
}
|
|
var output = Substring()
|
|
var index = number.endIndex
|
|
var count = 0
|
|
repeat {
|
|
index = number.index(before: index)
|
|
if count > 0, count % group == 0 {
|
|
output.insert("_", at: output.startIndex)
|
|
}
|
|
count += 1
|
|
output.insert(number[index], at: output.startIndex)
|
|
} while index != number.startIndex
|
|
number = String(output)
|
|
}
|
|
}
|