Skip to content

Commit 88beb85

Browse files
committed
Some small refectorings and updates from review feedback. Add a few more test cases. For formatting a selection (<#297>).
1 parent 6640067 commit 88beb85

File tree

13 files changed

+102
-71
lines changed

13 files changed

+102
-71
lines changed

Sources/SwiftFormat/API/Selection.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ public enum Selection {
1919
case ranges([Range<AbsolutePosition>])
2020

2121
/// Create a selection from an array of utf8 ranges. An empty array means an infinite selection.
22-
public init(offsetPairs: [Range<Int>]) {
23-
if offsetPairs.isEmpty {
22+
public init(offsetRanges: [Range<Int>]) {
23+
if offsetRanges.isEmpty {
2424
self = .infinite
2525
} else {
26-
let ranges = offsetPairs.map {
26+
let ranges = offsetRanges.map {
2727
AbsolutePosition(utf8Offset: $0.lowerBound) ..< AbsolutePosition(utf8Offset: $0.upperBound)
2828
}
2929
self = .ranges(ranges)
@@ -51,7 +51,7 @@ public enum Selection {
5151

5252

5353
public extension Syntax {
54-
/// return true if the node is _completely_ inside any range in the selection
54+
/// - Returns: `true` if the node is _completely_ inside any range in the selection
5555
func isInsideSelection(_ selection: Selection) -> Bool {
5656
switch selection {
5757
case .infinite:

Sources/SwiftFormat/API/SwiftFormatter.swift

-3
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,6 @@ public final class SwiftFormatter {
2121
/// The configuration settings that control the formatter's behavior.
2222
public let configuration: Configuration
2323

24-
/// the ranges of text to format
25-
public var selection: Selection = .infinite
26-
2724
/// An optional callback that will be notified with any findings encountered during formatting.
2825
public let findingConsumer: ((Finding) -> Void)?
2926

Sources/SwiftFormat/CMakeLists.txt

+1
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

+2-2
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ 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 optional ranges to process
42+
/// The selection to process
4343
let selection: Selection
4444

4545
/// Defines the operators and their precedence relationships that were used during parsing.
@@ -92,7 +92,7 @@ public final class Context {
9292

9393
/// Given a rule's name and the node it is examining, determine if the rule is disabled at this
9494
/// location or not. Also makes sure the entire node is contained inside any selection.
95-
func isRuleEnabled<R: Rule>(_ rule: R.Type, node: Syntax) -> Bool {
95+
func shouldFormat<R: Rule>(_ rule: R.Type, node: Syntax) -> Bool {
9696
guard node.isInsideSelection(selection) else { return false }
9797

9898
let loc = node.startLocation(converter: self.sourceLocationConverter)

Sources/SwiftFormat/Core/LintPipeline.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ extension LintPipeline {
2828
func visitIfEnabled<Rule: SyntaxLintRule, Node: SyntaxProtocol>(
2929
_ visitor: (Rule) -> (Node) -> SyntaxVisitorContinueKind, for node: Node
3030
) {
31-
guard context.isRuleEnabled(Rule.self, node: Syntax(node)) else { return }
31+
guard context.shouldFormat(Rule.self, node: Syntax(node)) else { return }
3232
let ruleId = ObjectIdentifier(Rule.self)
3333
guard self.shouldSkipChildren[ruleId] == nil else { return }
3434
let rule = self.rule(Rule.self)
@@ -54,7 +54,7 @@ extension LintPipeline {
5454
// more importantly because the `visit` methods return protocol refinements of `Syntax` that
5555
// cannot currently be expressed as constraints without duplicating this function for each of
5656
// them individually.
57-
guard context.isRuleEnabled(Rule.self, node: Syntax(node)) else { return }
57+
guard context.shouldFormat(Rule.self, node: Syntax(node)) else { return }
5858
guard self.shouldSkipChildren[ObjectIdentifier(Rule.self)] == nil else { return }
5959
let rule = self.rule(Rule.self)
6060
_ = visitor(rule)(node)

Sources/SwiftFormat/Core/SyntaxFormatRule.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public class SyntaxFormatRule: SyntaxRewriter, Rule {
3232
public override func visitAny(_ node: Syntax) -> Syntax? {
3333
// If the rule is not enabled, then return the node unmodified; otherwise, returning nil tells
3434
// SwiftSyntax to continue with the standard dispatch.
35-
guard context.isRuleEnabled(type(of: self), node: node) else { return node }
35+
guard context.shouldFormat(type(of: self), node: node) else { return node }
3636
return nil
3737
}
3838
}

Sources/SwiftFormat/PrettyPrint/PrettyPrint.swift

+36-45
Original file line numberDiff line numberDiff line change
@@ -69,16 +69,14 @@ public class PrettyPrinter {
6969
private var tokens: [Token]
7070
private var source: String
7171

72-
/// keep track of where formatting was disabled in the original source
72+
/// Keep track of where formatting was disabled in the original source
7373
///
7474
/// To format a selection, we insert `enableFormatting`/`disableFormatting` tokens into the
7575
/// stream when entering/exiting a selection range. Those tokens include utf8 offsets into the
7676
/// original source. When enabling formatting, we copy the text between `disabledPosition` and the
7777
/// current position to `outputBuffer`. From then on, we continue to format until the next
7878
/// `disableFormatting` token.
7979
private var disabledPosition: AbsolutePosition? = nil
80-
/// true if we're currently formatting
81-
private var writingIsEnabled: Bool { disabledPosition == nil }
8280

8381
private var outputBuffer: String = ""
8482

@@ -204,7 +202,9 @@ public class PrettyPrinter {
204202
///
205203
/// No further processing is performed on the string.
206204
private func writeRaw<S: StringProtocol>(_ str: S) {
207-
outputBuffer.append(String(str))
205+
if disabledPosition == nil {
206+
outputBuffer.append(String(str))
207+
}
208208
}
209209

210210
/// Writes newlines into the output stream, taking into account any preexisting consecutive
@@ -233,9 +233,7 @@ public class PrettyPrinter {
233233
}
234234

235235
guard numberToPrint > 0 else { return }
236-
if writingIsEnabled {
237-
writeRaw(String(repeating: "\n", count: numberToPrint))
238-
}
236+
writeRaw(String(repeating: "\n", count: numberToPrint))
239237
lineNumber += numberToPrint
240238
isAtStartOfLine = true
241239
consecutiveNewlineCount += numberToPrint
@@ -257,17 +255,13 @@ public class PrettyPrinter {
257255
/// leading spaces that are required before the text itself.
258256
private func write(_ text: String) {
259257
if isAtStartOfLine {
260-
if writingIsEnabled {
261-
writeRaw(currentIndentation.indentation())
262-
}
258+
writeRaw(currentIndentation.indentation())
263259
spaceRemaining = maxLineLength - currentIndentation.length(in: configuration)
264260
isAtStartOfLine = false
265-
} else if pendingSpaces > 0 && writingIsEnabled {
261+
} else if pendingSpaces > 0 {
266262
writeRaw(String(repeating: " ", count: pendingSpaces))
267263
}
268-
if writingIsEnabled {
269-
writeRaw(text)
270-
}
264+
writeRaw(text)
271265
consecutiveNewlineCount = 0
272266
pendingSpaces = 0
273267
}
@@ -546,9 +540,7 @@ public class PrettyPrinter {
546540
}
547541

548542
case .verbatim(let verbatim):
549-
if writingIsEnabled {
550-
writeRaw(verbatim.print(indent: currentIndentation))
551-
}
543+
writeRaw(verbatim.print(indent: currentIndentation))
552544
consecutiveNewlineCount = 0
553545
pendingSpaces = 0
554546
lastBreak = false
@@ -596,38 +588,37 @@ public class PrettyPrinter {
596588
}
597589

598590
case .enableFormatting(let enabledPosition):
599-
// if we're not disabled, we ignore the token
600-
if let disabledPosition {
601-
let start = source.utf8.index(source.utf8.startIndex, offsetBy: disabledPosition.utf8Offset)
602-
let end: String.Index
603-
if let enabledPosition {
604-
end = source.utf8.index(source.utf8.startIndex, offsetBy: enabledPosition.utf8Offset)
605-
} else {
606-
end = source.endIndex
607-
}
608-
var text = String(source[start..<end])
609-
// strip trailing whitespace so that the next formatting can add the right amount
610-
if let nonWhitespace = text.rangeOfCharacter(
611-
from: CharacterSet.whitespaces.inverted, options: .backwards) {
612-
text = String(text[..<nonWhitespace.upperBound])
613-
}
591+
guard let disabledPosition else {
592+
// if we're not disabled, we ignore the token
593+
break
594+
}
595+
let start = source.utf8.index(source.utf8.startIndex, offsetBy: disabledPosition.utf8Offset)
596+
let end: String.Index
597+
if let enabledPosition {
598+
end = source.utf8.index(source.utf8.startIndex, offsetBy: enabledPosition.utf8Offset)
599+
} else {
600+
end = source.endIndex
601+
}
602+
var text = String(source[start..<end])
603+
// strip trailing whitespace so that the next formatting can add the right amount
604+
if let nonWhitespace = text.rangeOfCharacter(
605+
from: CharacterSet.whitespaces.inverted, options: .backwards) {
606+
text = String(text[..<nonWhitespace.upperBound])
607+
}
614608

615-
writeRaw(text)
616-
if text.hasSuffix("\n") {
617-
isAtStartOfLine = true
618-
consecutiveNewlineCount = 1
619-
} else {
620-
isAtStartOfLine = false
621-
consecutiveNewlineCount = 0
622-
}
623-
self.disabledPosition = nil
609+
self.disabledPosition = nil
610+
writeRaw(text)
611+
if text.hasSuffix("\n") {
612+
isAtStartOfLine = true
613+
consecutiveNewlineCount = 1
614+
} else {
615+
isAtStartOfLine = false
616+
consecutiveNewlineCount = 0
624617
}
625618

626619
case .disableFormatting(let newPosition):
627-
// a second disable is ignored
628-
if writingIsEnabled {
629-
disabledPosition = newPosition
630-
}
620+
assert(disabledPosition == nil)
621+
disabledPosition = newPosition
631622
}
632623
}
633624

Sources/SwiftFormat/PrettyPrint/TokenStreamCreator.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -4071,7 +4071,7 @@ class CommentMovingRewriter: SyntaxRewriter {
40714071
self.selection = selection
40724072
}
40734073

4074-
var selection: Selection
4074+
private let selection: Selection
40754075

40764076
override func visit(_ node: SourceFileSyntax) -> SourceFileSyntax {
40774077
if shouldFormatterIgnore(file: node) {

Sources/SwiftFormat/Rules/OrderedImports.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ fileprivate func generateLines(codeBlockItemList: CodeBlockItemListSyntax, conte
310310
if currentLine.syntaxNode != nil {
311311
appendNewLine()
312312
}
313-
let sortable = context.isRuleEnabled(OrderedImports.self, node: Syntax(block))
313+
let sortable = context.shouldFormat(OrderedImports.self, node: Syntax(block))
314314
var blockWithoutTrailingTrivia = block
315315
blockWithoutTrailingTrivia.trailingTrivia = []
316316
currentLine.syntaxNode = .importCodeBlock(blockWithoutTrailingTrivia, sortable: sortable)

Sources/_SwiftFormatTestSupport/MarkedText.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public struct MarkedText {
4949

5050
self.markers = markers
5151
self.textWithoutMarkers = text
52-
self.selection = Selection(offsetPairs: offsets)
52+
self.selection = Selection(offsetRanges: offsets)
5353
}
5454
}
5555

Sources/swift-format/Frontend/Frontend.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ class Frontend {
127127

128128
var selection: Selection = .infinite
129129
if let offsets = lintFormatOptions.offsets {
130-
selection = Selection(offsetPairs: offsets)
130+
selection = Selection(offsetRanges: offsets)
131131
}
132132
let fileToProcess = FileToProcess(
133133
fileHandle: FileHandle.standardInput,
@@ -178,7 +178,7 @@ class Frontend {
178178

179179
var selection: Selection = .infinite
180180
if let offsets = lintFormatOptions.offsets {
181-
selection = Selection(offsetPairs: offsets)
181+
selection = Selection(offsetRanges: offsets)
182182
}
183183
return FileToProcess(
184184
fileHandle: sourceFile,

Sources/swift-format/Subcommands/LintFormatOptions.swift

+7-1
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ struct LintFormatOptions: ParsableArguments {
125125
}
126126
}
127127

128-
extension [Range<Int>] : @retroactive ExpressibleByArgument {
128+
extension [Range<Int>] {
129129
public init?(argument: String) {
130130
let pairs = argument.components(separatedBy: ",")
131131
let ranges: [Range<Int>] = pairs.compactMap {
@@ -139,3 +139,9 @@ extension [Range<Int>] : @retroactive ExpressibleByArgument {
139139
self = ranges
140140
}
141141
}
142+
143+
#if compiler(>=6)
144+
extension [Range<Int>] : @retroactive ExpressibleByArgument {}
145+
#else
146+
extension [Range<Int>] : ExpressibleByArgument {}
147+
#endif

Tests/SwiftFormatTests/PrettyPrint/SelectionTests.swift

+44-8
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ final class SelectionTests: PrettyPrintTestCase {
2121
}
2222
"""
2323

24-
// The line length ends on the last paren of .Stuff()
2524
assertPrettyPrintEqual(input: input, expected: expected, linelength: 80)
2625
}
2726

@@ -44,7 +43,6 @@ final class SelectionTests: PrettyPrintTestCase {
4443
}
4544
"""
4645

47-
// The line length ends on the last paren of .Stuff()
4846
assertPrettyPrintEqual(input: input, expected: expected, linelength: 80)
4947
}
5048

@@ -67,7 +65,6 @@ final class SelectionTests: PrettyPrintTestCase {
6765
}
6866
"""
6967

70-
// The line length ends on the last paren of .Stuff()
7168
assertPrettyPrintEqual(input: input, expected: expected, linelength: 80)
7269
}
7370

@@ -90,7 +87,6 @@ final class SelectionTests: PrettyPrintTestCase {
9087
}
9188
"""
9289

93-
// The line length ends on the last paren of .Stuff()
9490
assertPrettyPrintEqual(input: input, expected: expected, linelength: 80)
9591
}
9692

@@ -113,7 +109,6 @@ final class SelectionTests: PrettyPrintTestCase {
113109
}
114110
"""
115111

116-
// The line length ends on the last paren of .Stuff()
117112
assertPrettyPrintEqual(input: input, expected: expected, linelength: 80)
118113
}
119114

@@ -164,7 +159,6 @@ final class SelectionTests: PrettyPrintTestCase {
164159
}
165160
"""
166161

167-
// The line length ends on the last paren of .Stuff()
168162
assertPrettyPrintEqual(input: input, expected: expected, linelength: 80)
169163
}
170164

@@ -191,7 +185,50 @@ final class SelectionTests: PrettyPrintTestCase {
191185
}
192186
"""
193187

194-
// The line length ends on the last paren of .Stuff()
188+
assertPrettyPrintEqual(input: input, expected: expected, linelength: 80)
189+
}
190+
191+
func testSingleLineFunc() {
192+
let input =
193+
"""
194+
func foo() ⏩{}⏪
195+
"""
196+
197+
let expected =
198+
"""
199+
func foo() {}
200+
"""
201+
202+
assertPrettyPrintEqual(input: input, expected: expected, linelength: 80)
203+
}
204+
205+
func testSingleLineFunc2() {
206+
let input =
207+
"""
208+
func foo() /**/ ⏩{}⏪
209+
"""
210+
211+
let expected =
212+
"""
213+
func foo() /**/ {}
214+
"""
215+
216+
assertPrettyPrintEqual(input: input, expected: expected, linelength: 80)
217+
}
218+
219+
func testSimpleFunc() {
220+
let input =
221+
"""
222+
func foo() /**/
223+
⏩{}⏪
224+
"""
225+
226+
let expected =
227+
"""
228+
func foo() /**/
229+
{}
230+
"""
231+
195232
assertPrettyPrintEqual(input: input, expected: expected, linelength: 80)
196233
}
197234

@@ -219,7 +256,6 @@ final class SelectionTests: PrettyPrintTestCase {
219256
}
220257
"""
221258

222-
// The line length ends on the last paren of .Stuff()
223259
assertPrettyPrintEqual(input: input, expected: expected, linelength: 80)
224260
}
225261

0 commit comments

Comments
 (0)