Skip to content

Commit d11bc2a

Browse files
authored
Merge pull request #760 from ahoppen/6.0/cherry-pick-2024-06-27
Merge `main` into `release/6.0`
2 parents 9f89186 + c44c92b commit d11bc2a

30 files changed

+905
-198
lines changed

CODE_OF_CONDUCT.md

Lines changed: 0 additions & 55 deletions
This file was deleted.

CONTRIBUTING.md

Lines changed: 0 additions & 11 deletions
This file was deleted.

README.md

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,11 @@ For example, if you are using Xcode 13.3 (Swift 5.6), you will need
6161
## Getting swift-format
6262

6363
If you are mainly interested in using swift-format (rather than developing it),
64-
then you can get swift-format either via [Homebrew](https://brew.sh/) or by checking out the
65-
source and building it.
64+
then you can get it in three different ways:
65+
66+
### Included in Xcode
67+
68+
Xcode 16 and above include swift-format in the toolchain. You can run `swift-format` from anywhere on the system using `swift format` (notice the space instead of dash). To find the path at which `swift-format` is installed, run `xcrun --find swift-format`.
6669

6770
### Installing via Homebrew
6871

@@ -251,3 +254,24 @@ been merged into `main`.
251254

252255
If you are interested in developing `swift-format`, there is additional
253256
documentation about that [here](Documentation/Development.md).
257+
258+
## Contributing
259+
260+
Contributions to Swift are welcomed and encouraged! Please see the
261+
[Contributing to Swift guide](https://swift.org/contributing/).
262+
263+
Before submitting the pull request, please make sure you have [tested your
264+
changes](https://github.com/apple/swift/blob/main/docs/ContinuousIntegration.md)
265+
and that they follow the Swift project [guidelines for contributing
266+
code](https://swift.org/contributing/#contributing-code).
267+
268+
To be a truly great community, [Swift.org](https://swift.org/) needs to welcome
269+
developers from all walks of life, with different backgrounds, and with a wide
270+
range of experience. A diverse and friendly community will have more great
271+
ideas, more unique perspectives, and produce more great code. We will work
272+
diligently to make the Swift community welcoming to everyone.
273+
274+
To give clarity of what is expected of our members, Swift has adopted the
275+
code of conduct defined by the Contributor Covenant. This document is used
276+
across many open source communities, and we think it articulates our values
277+
well. For more, see the [Code of Conduct](https://swift.org/code-of-conduct/).
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 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+
import SwiftSyntax
15+
16+
/// The selection as given on the command line - an array of offets and lengths
17+
public enum Selection {
18+
case infinite
19+
case ranges([Range<AbsolutePosition>])
20+
21+
/// Create a selection from an array of utf8 ranges. An empty array means an infinite selection.
22+
public init(offsetRanges: [Range<Int>]) {
23+
if offsetRanges.isEmpty {
24+
self = .infinite
25+
} else {
26+
let ranges = offsetRanges.map {
27+
AbsolutePosition(utf8Offset: $0.lowerBound) ..< AbsolutePosition(utf8Offset: $0.upperBound)
28+
}
29+
self = .ranges(ranges)
30+
}
31+
}
32+
33+
public func contains(_ position: AbsolutePosition) -> Bool {
34+
switch self {
35+
case .infinite:
36+
return true
37+
case .ranges(let ranges):
38+
return ranges.contains { $0.contains(position) }
39+
}
40+
}
41+
42+
public func overlapsOrTouches(_ range: Range<AbsolutePosition>) -> Bool {
43+
switch self {
44+
case .infinite:
45+
return true
46+
case .ranges(let ranges):
47+
return ranges.contains { $0.overlapsOrTouches(range) }
48+
}
49+
}
50+
}
51+
52+
53+
public extension Syntax {
54+
/// - Returns: `true` if the node is _completely_ inside any range in the selection
55+
func isInsideSelection(_ selection: Selection) -> Bool {
56+
switch selection {
57+
case .infinite:
58+
return true
59+
case .ranges(let ranges):
60+
return ranges.contains { return $0.lowerBound <= position && endPosition <= $0.upperBound }
61+
}
62+
}
63+
}

Sources/SwiftFormat/API/SwiftFormatter.swift

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -70,6 +70,7 @@ public final class SwiftFormatter {
7070
try format(
7171
source: String(contentsOf: url, encoding: .utf8),
7272
assumingFileURL: url,
73+
selection: .infinite,
7374
to: &outputStream,
7475
parsingDiagnosticHandler: parsingDiagnosticHandler)
7576
}
@@ -86,6 +87,7 @@ public final class SwiftFormatter {
8687
/// - url: A file URL denoting the filename/path that should be assumed for this syntax tree,
8788
/// which is associated with any diagnostics emitted during formatting. If this is nil, a
8889
/// dummy value will be used.
90+
/// - selection: The ranges to format
8991
/// - outputStream: A value conforming to `TextOutputStream` to which the formatted output will
9092
/// be written.
9193
/// - parsingDiagnosticHandler: An optional callback that will be notified if there are any
@@ -94,6 +96,7 @@ public final class SwiftFormatter {
9496
public func format<Output: TextOutputStream>(
9597
source: String,
9698
assumingFileURL url: URL?,
99+
selection: Selection,
97100
to outputStream: inout Output,
98101
parsingDiagnosticHandler: ((Diagnostic, SourceLocation) -> Void)? = nil
99102
) throws {
@@ -108,8 +111,8 @@ public final class SwiftFormatter {
108111
assumingFileURL: url,
109112
parsingDiagnosticHandler: parsingDiagnosticHandler)
110113
try format(
111-
syntax: sourceFile, operatorTable: .standardOperators, assumingFileURL: url, source: source,
112-
to: &outputStream)
114+
syntax: sourceFile, source: source, operatorTable: .standardOperators, assumingFileURL: url,
115+
selection: selection, to: &outputStream)
113116
}
114117

115118
/// Formats the given Swift syntax tree and writes the result to an output stream.
@@ -122,32 +125,26 @@ public final class SwiftFormatter {
122125
///
123126
/// - Parameters:
124127
/// - syntax: The Swift syntax tree to be converted to source code and formatted.
128+
/// - source: The original Swift source code used to build the syntax tree.
125129
/// - operatorTable: The table that defines the operators and their precedence relationships.
126130
/// This must be the same operator table that was used to fold the expressions in the `syntax`
127131
/// argument.
128132
/// - url: A file URL denoting the filename/path that should be assumed for this syntax tree,
129133
/// which is associated with any diagnostics emitted during formatting. If this is nil, a
130134
/// dummy value will be used.
135+
/// - selection: The ranges to format
131136
/// - outputStream: A value conforming to `TextOutputStream` to which the formatted output will
132137
/// be written.
133138
/// - Throws: If an unrecoverable error occurs when formatting the code.
134139
public func format<Output: TextOutputStream>(
135-
syntax: SourceFileSyntax, operatorTable: OperatorTable, assumingFileURL url: URL?,
136-
to outputStream: inout Output
137-
) throws {
138-
try format(
139-
syntax: syntax, operatorTable: operatorTable, assumingFileURL: url, source: nil,
140-
to: &outputStream)
141-
}
142-
143-
private func format<Output: TextOutputStream>(
144-
syntax: SourceFileSyntax, operatorTable: OperatorTable,
145-
assumingFileURL url: URL?, source: String?, to outputStream: inout Output
140+
syntax: SourceFileSyntax, source: String, operatorTable: OperatorTable,
141+
assumingFileURL url: URL?, selection: Selection, to outputStream: inout Output
146142
) throws {
147143
let assumedURL = url ?? URL(fileURLWithPath: "source")
148144
let context = Context(
149145
configuration: configuration, operatorTable: operatorTable, findingConsumer: findingConsumer,
150-
fileURL: assumedURL, sourceFileSyntax: syntax, source: source, ruleNameCache: ruleNameCache)
146+
fileURL: assumedURL, selection: selection, sourceFileSyntax: syntax, source: source,
147+
ruleNameCache: ruleNameCache)
151148
let pipeline = FormatPipeline(context: context)
152149
let transformedSyntax = pipeline.rewrite(Syntax(syntax))
153150

@@ -158,6 +155,7 @@ public final class SwiftFormatter {
158155

159156
let printer = PrettyPrinter(
160157
context: context,
158+
source: source,
161159
node: transformedSyntax,
162160
printTokenStream: debugOptions.contains(.dumpTokenStream),
163161
whitespaceOnly: false)

Sources/SwiftFormat/API/SwiftLinter.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,17 +119,18 @@ public final class SwiftLinter {
119119
/// - Throws: If an unrecoverable error occurs when formatting the code.
120120
public func lint(
121121
syntax: SourceFileSyntax,
122+
source: String,
122123
operatorTable: OperatorTable,
123124
assumingFileURL url: URL
124125
) throws {
125-
try lint(syntax: syntax, operatorTable: operatorTable, assumingFileURL: url, source: nil)
126+
try lint(syntax: syntax, operatorTable: operatorTable, assumingFileURL: url, source: source)
126127
}
127128

128129
private func lint(
129130
syntax: SourceFileSyntax,
130131
operatorTable: OperatorTable,
131132
assumingFileURL url: URL,
132-
source: String?
133+
source: String
133134
) throws {
134135
let context = Context(
135136
configuration: configuration, operatorTable: operatorTable, findingConsumer: findingConsumer,
@@ -145,6 +146,7 @@ public final class SwiftLinter {
145146
// pretty-printer.
146147
let printer = PrettyPrinter(
147148
context: context,
149+
source: source,
148150
node: Syntax(syntax),
149151
printTokenStream: debugOptions.contains(.dumpTokenStream),
150152
whitespaceOnly: true)

Sources/SwiftFormat/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ add_library(SwiftFormat
1414
API/Finding.swift
1515
API/FindingCategorizing.swift
1616
API/Indent.swift
17+
API/Selection.swift
1718
API/SwiftFormatError.swift
1819
API/SwiftFormatter.swift
1920
API/SwiftLinter.swift

Sources/SwiftFormat/Core/Context.swift

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -39,6 +39,9 @@ public final class Context {
3939
/// The configuration for this run of the pipeline, provided by a configuration JSON file.
4040
let configuration: Configuration
4141

42+
/// The selection to process
43+
let selection: Selection
44+
4245
/// Defines the operators and their precedence relationships that were used during parsing.
4346
let operatorTable: OperatorTable
4447

@@ -66,6 +69,7 @@ public final class Context {
6669
operatorTable: OperatorTable,
6770
findingConsumer: ((Finding) -> Void)?,
6871
fileURL: URL,
72+
selection: Selection = .infinite,
6973
sourceFileSyntax: SourceFileSyntax,
7074
source: String? = nil,
7175
ruleNameCache: [ObjectIdentifier: String]
@@ -74,6 +78,7 @@ public final class Context {
7478
self.operatorTable = operatorTable
7579
self.findingEmitter = FindingEmitter(consumer: findingConsumer)
7680
self.fileURL = fileURL
81+
self.selection = selection
7782
self.importsXCTest = .notDetermined
7883
let tree = source.map { Parser.parse(source: $0) } ?? sourceFileSyntax
7984
self.sourceLocationConverter =
@@ -86,8 +91,10 @@ public final class Context {
8691
}
8792

8893
/// Given a rule's name and the node it is examining, determine if the rule is disabled at this
89-
/// location or not.
90-
func isRuleEnabled<R: Rule>(_ rule: R.Type, node: Syntax) -> Bool {
94+
/// location or not. Also makes sure the entire node is contained inside any selection.
95+
func shouldFormat<R: Rule>(_ rule: R.Type, node: Syntax) -> Bool {
96+
guard node.isInsideSelection(selection) else { return false }
97+
9198
let loc = node.startLocation(converter: self.sourceLocationConverter)
9299

93100
assert(

0 commit comments

Comments
 (0)