Skip to content

Commit 32d0ed9

Browse files
committed
Use string based SourceLocationConverter init because it's faster.
I'm not sure why, but the string based initializer for `SourceLocationConverter` is substantially faster than the syntax tree based initializer. The impact is a few hundred ms per file, so it is substantial when formatting many files. Results: - swift-protobuf 21.4 seconds before, 17.9 seconds after - 10k LOC file - 2.5 seconds before, 2.2 seconds after
1 parent 34ccba1 commit 32d0ed9

File tree

3 files changed

+30
-10
lines changed

3 files changed

+30
-10
lines changed

Sources/SwiftFormat/SwiftFormatter.swift

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ public final class SwiftFormatter {
5858
throw SwiftFormatError.isDirectory
5959
}
6060
let sourceFile = try SyntaxParser.parse(url)
61-
try format(syntax: sourceFile, assumingFileURL: url, to: &outputStream)
61+
let source = try String(contentsOf: url, encoding: .utf8)
62+
try format(syntax: sourceFile, assumingFileURL: url, source: source, to: &outputStream)
6263
}
6364

6465
/// Formats the given Swift source code and writes the result to an output stream.
@@ -75,11 +76,13 @@ public final class SwiftFormatter {
7576
source: String, assumingFileURL url: URL?, to outputStream: inout Output
7677
) throws {
7778
let sourceFile = try SyntaxParser.parse(source: source)
78-
try format(syntax: sourceFile, assumingFileURL: url, to: &outputStream)
79+
try format(syntax: sourceFile, assumingFileURL: url, source: source, to: &outputStream)
7980
}
8081

8182
/// Formats the given Swift syntax tree and writes the result to an output stream.
8283
///
84+
/// - Note: The formatter may be faster using the source text, if it's available.
85+
///
8386
/// - Parameters:
8487
/// - syntax: The Swift syntax tree to be converted to source code and formatted.
8588
/// - url: A file URL denoting the filename/path that should be assumed for this syntax tree,
@@ -90,6 +93,13 @@ public final class SwiftFormatter {
9093
/// - Throws: If an unrecoverable error occurs when formatting the code.
9194
public func format<Output: TextOutputStream>(
9295
syntax: SourceFileSyntax, assumingFileURL url: URL?, to outputStream: inout Output
96+
) throws {
97+
try format(syntax: syntax, assumingFileURL: url, source: nil, to: &outputStream)
98+
}
99+
100+
private func format<Output: TextOutputStream>(
101+
syntax: SourceFileSyntax, assumingFileURL url: URL?, source: String?,
102+
to outputStream: inout Output
93103
) throws {
94104
if let position = firstInvalidSyntaxPosition(in: Syntax(syntax)) {
95105
throw SwiftFormatError.fileContainsInvalidSyntax(position: position)
@@ -98,7 +108,7 @@ public final class SwiftFormatter {
98108
let assumedURL = url ?? URL(fileURLWithPath: "source")
99109
let context = Context(
100110
configuration: configuration, diagnosticEngine: diagnosticEngine, fileURL: assumedURL,
101-
sourceFileSyntax: syntax)
111+
sourceFileSyntax: syntax, source: source)
102112
let pipeline = FormatPipeline(context: context)
103113
let transformedSyntax = pipeline.visit(Syntax(syntax))
104114

Sources/SwiftFormat/SwiftLinter.swift

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ public final class SwiftLinter {
5454
throw SwiftFormatError.isDirectory
5555
}
5656
let sourceFile = try SyntaxParser.parse(url)
57-
try lint(syntax: sourceFile, assumingFileURL: url)
57+
let source = try String(contentsOf: url, encoding: .utf8)
58+
try lint(syntax: sourceFile, assumingFileURL: url, source: source)
5859
}
5960

6061
/// Lints the given Swift source code.
@@ -65,23 +66,30 @@ public final class SwiftLinter {
6566
/// - Throws: If an unrecoverable error occurs when formatting the code.
6667
public func lint(source: String, assumingFileURL url: URL) throws {
6768
let sourceFile = try SyntaxParser.parse(source: source)
68-
try lint(syntax: sourceFile, assumingFileURL: url)
69+
try lint(syntax: sourceFile, assumingFileURL: url, source: source)
6970
}
7071

7172
/// Lints the given Swift syntax tree.
7273
///
74+
/// - Note: The linter may be faster using the source text, if it's available.
75+
///
7376
/// - Parameters:
7477
/// - syntax: The Swift syntax tree to be converted to be linted.
7578
/// - url: A file URL denoting the filename/path that should be assumed for this syntax tree.
79+
/// - source: The Swift source code. This can be provided to improve the linter's performance.
7680
/// - Throws: If an unrecoverable error occurs when formatting the code.
7781
public func lint(syntax: SourceFileSyntax, assumingFileURL url: URL) throws {
82+
try lint(syntax: syntax, assumingFileURL: url, source: nil)
83+
}
84+
85+
private func lint(syntax: SourceFileSyntax, assumingFileURL url: URL, source: String?) throws {
7886
if let position = firstInvalidSyntaxPosition(in: Syntax(syntax)) {
7987
throw SwiftFormatError.fileContainsInvalidSyntax(position: position)
8088
}
81-
89+
8290
let context = Context(
8391
configuration: configuration, diagnosticEngine: diagnosticEngine, fileURL: url,
84-
sourceFileSyntax: syntax)
92+
sourceFileSyntax: syntax, source: source)
8593
let pipeline = LintPipeline(context: context)
8694
pipeline.walk(Syntax(syntax))
8795

Sources/SwiftFormatCore/Context.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,16 @@ public class Context {
5757
configuration: Configuration,
5858
diagnosticEngine: DiagnosticEngine?,
5959
fileURL: URL,
60-
sourceFileSyntax: SourceFileSyntax
60+
sourceFileSyntax: SourceFileSyntax,
61+
source: String? = nil
6162
) {
6263
self.configuration = configuration
6364
self.diagnosticEngine = diagnosticEngine
6465
self.fileURL = fileURL
6566
self.importsXCTest = .notDetermined
66-
self.sourceLocationConverter = SourceLocationConverter(
67-
file: fileURL.path, tree: sourceFileSyntax)
67+
self.sourceLocationConverter =
68+
source.map { SourceLocationConverter(file: fileURL.path, source: $0) }
69+
?? SourceLocationConverter(file: fileURL.path, tree: sourceFileSyntax)
6870
self.ruleMask = RuleMask(
6971
syntaxNode: Syntax(sourceFileSyntax),
7072
sourceLocationConverter: sourceLocationConverter

0 commit comments

Comments
 (0)