Skip to content

Commit 768acf7

Browse files
committed
Added ANSIColor and ColoredANSIStream for printing colored diagnostic messages
1 parent 0f32b8a commit 768acf7

File tree

3 files changed

+140
-3
lines changed

3 files changed

+140
-3
lines changed

Tests/LLVMTests/ANSIColor.swift

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import Foundation
2+
3+
/// Represents the possible ANSI color codes.
4+
enum ANSIColor: String {
5+
case black = "\u{001B}[30m"
6+
case red = "\u{001B}[31m"
7+
case green = "\u{001B}[32m"
8+
case yellow = "\u{001B}[33m"
9+
case blue = "\u{001B}[34m"
10+
case magenta = "\u{001B}[35m"
11+
case cyan = "\u{001B}[36m"
12+
case white = "\u{001B}[37m"
13+
case bold = "\u{001B}[1m"
14+
case reset = "\u{001B}[0m"
15+
16+
func name() -> String {
17+
switch self {
18+
case .black: return "Black"
19+
case .red: return "Red"
20+
case .green: return "Green"
21+
case .yellow: return "Yellow"
22+
case .blue: return "Blue"
23+
case .magenta: return "Magenta"
24+
case .cyan: return "Cyan"
25+
case .white: return "White"
26+
case .bold: return "Bold"
27+
case .reset: return "Reset"
28+
}
29+
}
30+
31+
static func all() -> [ANSIColor] {
32+
return [.black, .red, .green,
33+
.yellow, .blue, .magenta,
34+
.cyan, .white, .bold, .reset]
35+
}
36+
}
37+
38+
func + (left: ANSIColor, right: String) -> String {
39+
return left.rawValue + right
40+
}

Tests/LLVMTests/ColoredStream.swift

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import Foundation
2+
3+
/// Represents a stream that can write text with a specific set of ANSI colors
4+
protocol ColoredStream: TextOutputStream {
5+
mutating func write(_ string: String, with: [ANSIColor])
6+
}
7+
8+
extension ColoredStream {
9+
/// Default conformance to TextOutputStream
10+
mutating func write(_ string: String) {
11+
write(string, with: [])
12+
}
13+
}
14+
15+
/// An output stream that prints to an underlying stream including ANSI color
16+
/// codes.
17+
class ColoredANSIStream<StreamTy: TextOutputStream>: ColoredStream {
18+
19+
typealias StreamType = StreamTy
20+
21+
var currentColors = [ANSIColor]()
22+
var stream: StreamType
23+
let colored: Bool
24+
25+
/// Creates a new ColoredANSIStream that prints to an underlying stream.
26+
///
27+
/// - Parameters:
28+
/// - stream: The underlying stream
29+
/// - colored: Whether to provide any colors or to pass text through
30+
/// unmodified. Set this to false and ColoredANSIStream is
31+
/// a transparent wrapper.
32+
init(_ stream: inout StreamType, colored: Bool = true) {
33+
self.stream = stream
34+
self.colored = colored
35+
}
36+
37+
/// Initializes with a stream, always colored.
38+
///
39+
/// - Parameter stream: The underlying stream receiving writes.
40+
required init(_ stream: inout StreamType) {
41+
self.stream = stream
42+
self.colored = true
43+
}
44+
45+
/// Adds a color to the in-progress colors.
46+
func addColor(_ color: ANSIColor) {
47+
guard colored else { return }
48+
stream.write(color.rawValue)
49+
currentColors.append(color)
50+
}
51+
52+
/// Resets this stream back to the default color.
53+
func reset() {
54+
if currentColors.isEmpty { return }
55+
stream.write(ANSIColor.reset.rawValue)
56+
currentColors = []
57+
}
58+
59+
/// Sets the current ANSI color codes to the passed-in colors.
60+
func setColors(_ colors: [ANSIColor]) {
61+
guard colored else { return }
62+
reset()
63+
for color in colors {
64+
stream.write(color.rawValue)
65+
}
66+
currentColors = colors
67+
}
68+
69+
/// Writes the string to the output with the provided colors.
70+
///
71+
/// - Parameters:
72+
/// - string: The string to write
73+
/// - colors: The colors used on the string
74+
func write(_ string: String, with colors: [ANSIColor]) {
75+
self.setColors(colors)
76+
stream.write(string)
77+
self.reset()
78+
}
79+
}

Tests/LLVMTests/FileCheck.swift

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1224,16 +1224,34 @@ private struct CheckString {
12241224
}
12251225
}
12261226

1227-
private enum DiagnosticKind {
1227+
private enum DiagnosticKind: String {
12281228
case error
12291229
case warning
12301230
case note
1231+
1232+
var color: ANSIColor {
1233+
switch self {
1234+
case .error: return .red
1235+
case .warning: return .magenta
1236+
case .note: return .green
1237+
}
1238+
}
12311239
}
12321240

1241+
struct StdOutStream: TextOutputStream {
1242+
mutating func write(_ string: String) {
1243+
print(string, terminator: "")
1244+
}
1245+
}
1246+
1247+
var stdoutStream = StdOutStream()
1248+
var diagnosticStream = ColoredANSIStream(&stdoutStream)
1249+
12331250
private func diagnose(_ kind : DiagnosticKind, _ loc : CheckLoc, _ message : String) {
1234-
print(message)
1251+
diagnosticStream.write("\(kind): ", with: [.bold, kind.color])
1252+
diagnosticStream.write("\(message)\n", with: [.bold])
12351253
let msg = loc.message
12361254
if !msg.isEmpty {
1237-
print(msg)
1255+
diagnosticStream.write("\(msg)\n")
12381256
}
12391257
}

0 commit comments

Comments
 (0)