Skip to content

Commit a86e374

Browse files
committed
Xcode diagnostics
1 parent cb31f49 commit a86e374

4 files changed

Lines changed: 44 additions & 14 deletions

File tree

Plugins/OtterPlugin/OtterPlugin.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ struct OtterPlugin: BuildToolPlugin {
4141
projectRoot.absoluteString,
4242
"--override-output",
4343
queries.absoluteString,
44-
"--skip-directory-create"
44+
"--skip-directory-create",
45+
"--xcode-diagnostic-reporter"
4546
],
4647
inputFiles: sourceFiles?
4748
.filter { $0.url.pathExtension == "sql" }

Sources/Compiler/DiagnosticReporter.swift

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,23 @@
55
// Created by Wes Wickwire on 5/3/25.
66
//
77

8+
import Foundation
9+
810
public protocol DiagnosticReporter {
9-
func report(diagnostic: Diagnostic, source: String, fileName: String)
11+
func report(diagnostic: Diagnostic, source: String, filePath: String)
1012
}
1113

1214
extension DiagnosticReporter {
13-
func report(diagnostics: Diagnostics, source: String, fileName: String) {
15+
func report(diagnostics: Diagnostics, source: String, filePath: String) {
1416
for diagnostic in diagnostics.sorted(by: { $0.location.lowerBound < $1.location.lowerBound }) {
15-
report(diagnostic: diagnostic, source: source, fileName: fileName)
17+
report(diagnostic: diagnostic, source: source, filePath: filePath)
1618
}
1719
}
1820
}
1921

20-
public struct StdoutDiagnosticReporter: DiagnosticReporter {
22+
public final class StdoutDiagnosticReporter: DiagnosticReporter {
2123
private let dontColorize: Bool
24+
private var stderr = FileHandle.standardError
2225

2326
public init(dontColorize: Bool = false) {
2427
self.dontColorize = dontColorize
@@ -40,7 +43,8 @@ public struct StdoutDiagnosticReporter: DiagnosticReporter {
4043
dontColorize ? ("", "") : Self.bold
4144
}
4245

43-
public func report(diagnostic: Diagnostic, source: String, fileName: String) {
46+
public func report(diagnostic: Diagnostic, source: String, filePath: String) {
47+
let fileName = filePath.split(separator: "/").last ?? ""
4448
let range = diagnostic.location.range
4549
let start = startOfLine(index: range.lowerBound, source: source)
4650
// Note: This uses `lowerBound` as well to make sure we only get one line
@@ -59,7 +63,7 @@ public struct StdoutDiagnosticReporter: DiagnosticReporter {
5963
}
6064

6165
print("""
62-
\(fileName):\(line):\(column): \(bold.open)\(color.open)\(diagnostic.level)\(color.close)\(bold.close)
66+
\(fileName):\(line):\(column): \(bold.open)\(color.open)\(diagnostic.level)\(color.close)\(bold.close):
6367
6468
\(source)
6569
\(indent)\(color.open)\(underline)\(color.close) - \(bold.open)\(diagnostic.message)\(bold.close)
@@ -90,3 +94,22 @@ public struct StdoutDiagnosticReporter: DiagnosticReporter {
9094
return index
9195
}
9296
}
97+
98+
public final class XcodeDiagnosticReporter: DiagnosticReporter {
99+
private var stderr = FileHandle.standardError
100+
101+
public init() {}
102+
103+
public func report(diagnostic: Diagnostic, source: String, filePath: String) {
104+
let line = diagnostic.location.line
105+
let column = diagnostic.location.column
106+
print("\(filePath):\(line):\(column): \(diagnostic.level): \(diagnostic.message)", to: &stderr)
107+
}
108+
}
109+
110+
extension FileHandle: TextOutputStream {
111+
public func write(_ string: String) {
112+
let data = Data(string.utf8)
113+
self.write(data)
114+
}
115+
}

Sources/Compiler/Driver.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ public actor Driver {
9191
let hasDiagnostics = results.contains { $0.value.diagnostics.contains { $0.level == .error } }
9292

9393
guard !hasDiagnostics else {
94-
return // Just skip, diagnostics should have already been emitted.
94+
exit(1) // Diagnostics should have already been emitted.
9595
}
9696

9797
let lang = Lang(options: options)
@@ -140,7 +140,7 @@ public actor Driver {
140140
}
141141
}
142142

143-
report(diagnostics: diagnostics, source: fileContents, fileName: file)
143+
report(diagnostics: diagnostics, source: fileContents, filePath: path)
144144

145145
results[file] = Output(
146146
fileName: file,
@@ -157,9 +157,9 @@ public actor Driver {
157157
}
158158
}
159159

160-
private func report(diagnostics: Diagnostics, source: String, fileName: String) {
160+
private func report(diagnostics: Diagnostics, source: String, filePath: String) {
161161
for reporter in reporters {
162-
reporter.report(diagnostics: diagnostics, source: source, fileName: fileName)
162+
reporter.report(diagnostics: diagnostics, source: source, filePath: filePath)
163163
}
164164
}
165165

@@ -211,3 +211,4 @@ public actor Driver {
211211
return result
212212
}
213213
}
214+
struct Foot: Swift.Error {}

Sources/OtterCLI/GenerateCommand.swift

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ struct GenerateCommand: AsyncParsableCommand {
2727

2828
@Flag(help: "If true, the directory the output exists in will not be created if it doesn't exist")
2929
var skipDirectoryCreate = false
30+
31+
@Flag(help: "If true, it will emit diagnostics that Xcode can understand")
32+
var xcodeDiagnosticReporter = false
3033

3134
mutating func run() async throws {
3235
let config = try Config(at: path)
@@ -57,9 +60,11 @@ struct GenerateCommand: AsyncParsableCommand {
5760
let driver = Driver()
5861
await driver.logTimes(time)
5962

60-
await driver.add(
61-
reporter: StdoutDiagnosticReporter(dontColorize: dontColorize)
62-
)
63+
if xcodeDiagnosticReporter {
64+
await driver.add(reporter: XcodeDiagnosticReporter())
65+
} else {
66+
await driver.add(reporter: StdoutDiagnosticReporter(dontColorize: dontColorize))
67+
}
6368

6469
try await driver.compile(
6570
migrationsPath: project.migrationsDirectory.path,

0 commit comments

Comments
 (0)