Skip to content

Commit 84ffa17

Browse files
committed
Refactor DiagnosticsFormatter and extend its functionality
This commit refactors the existing code for `DiagnosticsFormatter` and introduces several new features, complete with documentation and unit tests. Key Enhancements: 1. Nested Diagnostic Support: Enhanced to include not only top-level diagnostics but also related notes, improving the debugging experience. 2. Custom Decorators: Incorporate the `DiagnosticDecorator` protocol, allowing for custom formatting and styling of diagnostic output. 3. Context Size Control: Added options to control the `ContextSize`, providing more flexibility in how much source code context is displayed around each diagnostic. Documentation: - Comprehensive documentation added, detailing the purpose, usage examples, and future developments for `DiagnosticsFormatter`. Testing: - Added robust unit tests to validate the new features and ensure reliability. This refactor and feature addition make `DiagnosticsFormatter` a more versatile and developer-friendly tool for debugging and understanding Swift code.
1 parent 09e5675 commit 84ffa17

File tree

11 files changed

+1119
-421
lines changed

11 files changed

+1119
-421
lines changed

Sources/SwiftDiagnostics/DiagnosticsFormatter.swift

Lines changed: 681 additions & 349 deletions
Large diffs are not rendered by default.

Sources/SwiftDiagnostics/GroupedDiagnostics.swift

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ extension GroupedDiagnostics {
172172
tree: sourceFile.tree
173173
)
174174

175-
let colorizeBufferOutline = formatter.colorizeBufferOutline
175+
let decorateBufferOutline = formatter.diagnosticDecorator.decorateBufferOutline
176176

177177
let childPadding = String(slc.sourceLines.count + 1).count + 1;
178178

@@ -200,7 +200,8 @@ extension GroupedDiagnostics {
200200
let location = primaryDiag.location(converter: primaryDiagSLC)
201201

202202
// Display file/line/column and diagnostic text for the primary diagnostic.
203-
prefixString = "\(location.file):\(location.line):\(location.column): \(formatter.colorizeIfRequested(primaryDiag.diagMessage))\n"
203+
prefixString =
204+
"\(location.file):\(location.line):\(location.column): \(formatter.diagnosticDecorator.decorateDiagnosticMessage(primaryDiag.diagMessage))\n"
204205

205206
// If the primary diagnostic source file is not the same as the root source file, we're pointing into a generated buffer.
206207
// Provide a link back to the original source file where this generated buffer occurred, so it's easy to find if
@@ -216,7 +217,7 @@ extension GroupedDiagnostics {
216217

217218
if rootSourceID == sourceFileID {
218219
let bufferLoc = slc.location(for: rootPosition)
219-
let coloredMessage = formatter.colorizeIfRequested(severity: .note, message: "expanded code originates here")
220+
let coloredMessage = formatter.diagnosticDecorator.decorateMessage("expanded code originates here", basedOnSeverity: .note)
220221
prefixString += "╰─ \(bufferLoc.file):\(bufferLoc.line):\(bufferLoc.column): \(coloredMessage)\n"
221222
}
222223
}
@@ -234,41 +235,44 @@ extension GroupedDiagnostics {
234235
let extraLengthNeeded = targetLineLength - padding.count - sourceFile.displayName.count - 6
235236
let boxSuffix: String
236237
if extraLengthNeeded > 0 {
237-
boxSuffix = colorizeBufferOutline(String(repeating: "", count: extraLengthNeeded))
238+
boxSuffix = decorateBufferOutline(String(repeating: "", count: extraLengthNeeded))
238239
} else {
239240
boxSuffix = ""
240241
}
241242

242-
prefixString = colorizeBufferOutline(padding + "╭─── ") + sourceFile.displayName + " " + boxSuffix + "\n"
243-
suffixString = colorizeBufferOutline(padding + "╰───" + String(repeating: "", count: sourceFile.displayName.count + 2)) + boxSuffix + "\n"
243+
prefixString = decorateBufferOutline(padding + "╭─── ") + sourceFile.displayName + " " + boxSuffix + "\n"
244+
suffixString = decorateBufferOutline(padding + "╰───" + String(repeating: "", count: sourceFile.displayName.count + 2)) + boxSuffix + "\n"
244245
}
245246

246247
// Render the buffer.
247248
return prefixString
248249
+ formatter.annotatedSource(
249-
tree: sourceFile.tree,
250-
diags: sourceFile.diagnostics,
251-
indentString: colorizeBufferOutline(indentString),
252-
suffixTexts: childSources,
253-
sourceLocationConverter: slc
254-
) + suffixString
250+
inSyntaxTree: sourceFile.tree,
251+
withDiagnostics: sourceFile.diagnostics,
252+
usingIndentString: decorateBufferOutline(indentString),
253+
appendingSuffixTexts: childSources,
254+
employingSourceLocationConverter: slc
255+
)
256+
+ suffixString
255257
}
256258
}
257259

258260
extension DiagnosticsFormatter {
259261
/// Annotate all of the source files in the given set of grouped diagnostics.
260262
public func annotateSources(in group: GroupedDiagnostics) -> String {
261-
return group.rootSourceFiles.map { rootSourceFileID in
262-
group.annotateSource(rootSourceFileID, formatter: self, indentString: "")
263-
}.joined(separator: "\n")
263+
group.rootSourceFiles
264+
.map { rootSourceFileID in
265+
group.annotateSource(rootSourceFileID, formatter: self, indentString: "")
266+
}
267+
.joined(separator: "\n")
264268
}
265269

266270
public static func annotateSources(
267271
in group: GroupedDiagnostics,
268-
contextSize: Int = 2,
269-
colorize: Bool = false
272+
contextSize: ContextSize = .limited(2),
273+
diagnosticDecorator: DiagnosticDecorator = ANSIDiagnosticDecorator(colorize: false)
270274
) -> String {
271-
let formatter = DiagnosticsFormatter(contextSize: contextSize, colorize: colorize)
275+
let formatter = DiagnosticsFormatter(contextSize: contextSize, diagnosticDecorator: diagnosticDecorator)
272276
return formatter.annotateSources(in: group)
273277
}
274278
}

Sources/SwiftSyntaxBuilder/Syntax+StringInterpolation.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ struct SyntaxStringInterpolationDiagnosticError: Error, CustomStringConvertible
185185
var description: String {
186186
// Start the diagnostic on a new line so it isn't prefixed with the file, which messes up the
187187
// column-aligned message from ``DiagnosticsFormatter``.
188-
return "\n" + DiagnosticsFormatter.annotatedSource(tree: tree, diags: diagnostics)
188+
return "\n" + DiagnosticsFormatter.annotatedSource(inSyntaxTree: tree, withDiagnostics: diagnostics)
189189
}
190190
}
191191

Sources/SwiftSyntaxBuilder/SyntaxParsable+ExpressibleByStringInterpolation.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ extension SyntaxParseable {
4747
!suppressStringInterpolationParsingErrors
4848
{
4949
let diagnostics = ParseDiagnosticsGenerator.diagnostics(for: self)
50-
let formattedDiagnostics = DiagnosticsFormatter().annotatedSource(tree: self, diags: diagnostics)
50+
let formattedDiagnostics = DiagnosticsFormatter.annotatedSource(inSyntaxTree: self, withDiagnostics: diagnostics)
5151
Logger(subsystem: "SwiftSyntax", category: "ParseError").fault(
5252
"""
5353
Parsing a `\(Self.self)` node from string interpolation produced the following parsing errors.

Sources/SwiftSyntaxMacrosTestSupport/Assertions.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ public func assertMacroExpansion(
281281
XCTFail(
282282
"""
283283
Expanded source should not contain any syntax errors, but contains:
284-
\(DiagnosticsFormatter.annotatedSource(tree: expandedSourceFile, diags: diags))
284+
\(DiagnosticsFormatter.annotatedSource(inSyntaxTree: expandedSourceFile, withDiagnostics: diags))
285285
286286
Expanded syntax tree was:
287287
\(expandedSourceFile.debugDescription)

Sources/swift-parser-cli/BasicFormat.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,16 @@ struct BasicFormat: ParsableCommand, ParseCommand {
7474
}
7575

7676
if resultTree.hasError {
77-
let diags = ParseDiagnosticsGenerator.diagnostics(for: tree)
77+
let diagnostics = ParseDiagnosticsGenerator.diagnostics(for: tree)
78+
let annotatedSource = DiagnosticsFormatter.annotatedSource(
79+
inSyntaxTree: tree,
80+
withDiagnostics: diagnostics,
81+
withDiagnosticDecorator: .ANSI(colorize: TerminalHelper.isConnectedToTerminal)
82+
)
7883
printerr(
7984
"""
8085
Source input contained syntax errors. Formatting might be incorrect due to these errors:
81-
\(DiagnosticsFormatter(colorize: TerminalHelper.isConnectedToTerminal).annotatedSource(tree: tree, diags: diags))
86+
\(annotatedSource)
8287
----------------------------------------
8388
"""
8489
)

Sources/swift-parser-cli/Commands/PrintDiags.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ struct PrintDiags: ParsableCommand, ParseCommand {
3939
group.addSourceFile(tree: tree, displayName: sourceFileName, diagnostics: diags)
4040
let annotatedSource = DiagnosticsFormatter.annotateSources(
4141
in: group,
42-
colorize: colorize || TerminalHelper.isConnectedToTerminal
42+
diagnosticDecorator: .ANSI(colorize: (colorize || TerminalHelper.isConnectedToTerminal))
4343
)
4444

4545
print(annotatedSource)

Sources/swift-parser-cli/Commands/VerifyRoundTrip.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ struct VerifyRoundTrip: ParsableCommand, ParseCommand {
6868
resultTree = Syntax(tree)
6969
}
7070

71-
_ = DiagnosticsFormatter.annotatedSource(tree: tree, diags: diags)
71+
_ = DiagnosticsFormatter.annotatedSource(inSyntaxTree: tree, withDiagnostics: diags)
7272

7373
if resultTree.syntaxTextBytes != [UInt8](source) {
7474
throw Error.roundTripFailed

0 commit comments

Comments
 (0)