Skip to content

Implement support for automatically coloring output in DiagnosticsFormatter #1280

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

simba909
Copy link
Contributor

This builds on the work in #1256 to add support for automatically enabling output coloring as suggested by @ahoppen. As of this change when invoking $ swift-parser-cli print-diags in a terminal it will enable output coloring by default. The --colorize flag introduced in #1256 is still supported to force output colors, if desired.

API-wise this should be a non-breaking change since it only adds overloads to instantiate a DiagnosticsFormatter with a new ColorMode parameter (shamelessly stolen from swift-format).

Some notes:

  • This implementation only copies the absolutely minimum required components from swift-format and swift-tools-support-core to enable the feature. I'm happy to discuss moving more things over from TSC / laying the groundwork for further development if that improves the end result.
    • In particular, TSC wraps stderr with a few wrapper types to simplify reading/writing to it. This doesn't seem to be used for detecting a TTY environment which is why I left it out for now.
  • I noticed that swift-tools-support-core exposes a TSCLibc module that re-exports symbols from C standard libraries on the various platforms (Glibc for Linux, CRT on Windows and Darwin.C on Apple platforms) but opted to "inline" the use of stderr for now. Don't have the resources at the moment to test this cross platform and so would more than appreciate any pointers here.
  • The implementation in TSCBasic uses a ProcessEnv type to read properties from the environment (relevant in this case is the ProcessEnv.vars["TERM"] call to check whether it's set to "dumb") on non-Windows platforms. I've opted to remove support for detecting this case for now since copying ProcessEnv into swift-syntax seems out of scope but please do let me know if there's anything I'm missing here.

Additionally, I've taken the liberty of adding "error:" and "warning:" prefixes to formatted output when coloring is enabled to help with accessibility, as suggested by @DougGregor in #1256. I realize it's slightly out of scope but it's a nice addition in my opinion, let me know if this is better suited for a separate PR.

@DougGregor
Copy link
Member

@swift-ci please test

@simba909
Copy link
Contributor Author

Looks like tests are failing because I forgot to add TerminalUtils.swift to CMakeLists, I've pushed a new commit that fixes this.

@DougGregor
Copy link
Member

@swift-ci please test

@DougGregor
Copy link
Member

Ah, the remaining macOS failure is because we've been trying not to introduce a Darwin dependency into the parts of swift-syntax that get linked into the compiler.

@DougGregor
Copy link
Member

I have a concrete suggestion. How about we move the TTY detection logic down into swift-parser-cli, which is allowed to have more dependencies (on Foundation, system packages like Darwin, etc.), and have the SwiftDiagnostics formatter code only take a Bool for whether the output should be colorized or not.

@simba909 simba909 force-pushed the feature/automagically-colored-diagnostics branch from 24f60c9 to 81c8740 Compare January 26, 2023 20:10
@simba909
Copy link
Contributor Author

@DougGregor thank you for reviewing! I've moved the TTY detection logic and made the warning: prefix bold.

@DougGregor
Copy link
Member

@swift-ci please test

1 similar comment
@DougGregor
Copy link
Member

@swift-ci please test

Copy link
Member

@DougGregor DougGregor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome, thanks!

Copy link
Member

@ahoppen ahoppen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. It’s nice to see colors automatically enabled.

let annotation = ANSIAnnotation(color: .yellow)
return annotation.applied(to: message.message)
let color = ANSIAnnotation(color: .yellow)
let prefix = color.withTrait(.bold).applied(to: "warning: ")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to be clear: It’s intentional that errors have both the prefix and the the message in bold but warnings only have a bold prefix? I’m fine with either way but just want to make sure the current design is a conscious decision.

return annotation.applied(to: message.message)
let color = ANSIAnnotation(color: .yellow)
let prefix = color.withTrait(.bold).applied(to: "warning: ")
return prefix + color.applied(to: message.message)
case .note:
return message.message
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also add a prefix for notes?

Comment on lines +28 to +30
static var isConnectedToTerminal: Bool {
return isTTY(stderr)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we are printing the errors to stdout (instead of stderr like swift-format), we should be checking whether stdout is connected to a terminal here. For example, stdout could be connected to a terminal while stderr is not if you do swift-parser-cli … 2> /tmp/errors.txt.

And, to be clear, we should probably name the method to mention that is checking stdout instead of stderr.

Suggested change
static var isConnectedToTerminal: Bool {
return isTTY(stderr)
}
static var isStdoutConnectedToTerminal: Bool {
return isTTY(stdout)
}

#if os(Android)
typealias FILEPointer = OpaquePointer
#else
typealias FILEPointer = UnsafeMutablePointer<FILE>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be named FilePointer to look a little more swift? It’s bad enough to have FILE scream at you in all caps.

@@ -198,17 +198,22 @@ class PrintDiags: ParsableCommand {
@Flag(name: .long, help: "Perform sequence folding with the standard operators")
var foldSequences: Bool = false

@Flag(name: .long, help: "Colorize output with ANSI color codes")
@Flag(name: .long, help: "Force output coloring with ANSI color codes")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could make this a flag with inversion: .prefixedNo like in https://github.com/apple/swift-format/blob/072768f1af50989996e137919f9f6be71e8294e6/Sources/swift-format/Subcommands/LintFormatOptions.swift#L62-L69 so you can also force coloring off if you want to.


And now that I think about it, we should name the flag color-diagnostics (just rename the variable to colorDiagnostics) to match the name of the flag in the Swift Compiler.

Suggested change
@Flag(name: .long, help: "Force output coloring with ANSI color codes")
@Flag(name: .long, inversion: .prefixedLo, help: "Enable or disable coloring with ANSI color codes")
let colorDiagnostics: Bool?

@DougGregor
Copy link
Member

@ahoppen are you okay with those suggestions being addressed in a follow-up PR?

@DougGregor
Copy link
Member

I'm going to merge because this is progress, and we can address feedback in a follow-up.

@DougGregor DougGregor merged commit def3b95 into swiftlang:main Jan 27, 2023
@simba909 simba909 deleted the feature/automagically-colored-diagnostics branch December 8, 2023 14:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants