-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
[WIP] Added colour to post-compile errors #5325
Conversation
@@ -31,6 +31,16 @@ struct CallStack | |||
dir | |||
end | |||
|
|||
# ANSI color and formatting escape codes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use Colorize
for that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait, there's a builtin library for this in Crystal? Huh, did not know, will fix ASAP.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also @Sija, do you feel that the current colour scheme for errors is appropriate?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A screenshot would help
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is Colorize
but it should not be used for this. Including it here would make it a mandatory dependency for each Crystal program. This should be avoided and as the current code shows, it's certainly easy enough to do without using a specialized component.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@RX14 Here is the error log with this program (I placed it in tmp/temp.cr
):
def foo
bar
end
def bar
a = [1]
a[2]
end
foo
Colorized output should not be mandatory for every use case. For example, you might not want ANSI command sequences in stderr that is piped to a logfile. There should at least be a option to disable this, but it would be nice if it would include some tty detection (like Implementing colored output is easy, but figuring out when to apply is the real challenge. It's certainly worth looking at solutions used in related projects. |
@straight-shoota Definitely. I'll have a look at Crystal's pre-compile errors to see what's happening there. I'll move the ANSI flags in I'm not sure if any existing languages have post-compile flags that are colorised with detail (i.e. more detail than Python's errors, which are just completely red). I'll try to find some examples. Also, should colorized output be opt-in or opt-out? |
Should I include post-compile errors into the |
@@ -152,6 +162,14 @@ struct CallStack | |||
end | |||
end | |||
|
|||
private def colorize_address(address) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method could be more generalized and used in all places in both files. It should probably be an (undocumented) class method on Exception
.
def Exception.colorize(code, string)
if colorize?
"#{code}#{string}\e[0m"
else
string
end
end
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added this method into Exception
with the new commit!
I don't think a compiler flag is the right thing for this because colour mode needs to be determined/configurable at runtime. As a quick sketch, I'd suggest something similar like:
|
It's definitely nice, but I believe mixing bold / non bold is more disturbing than helpful. I'd stick to non-bold colors maybe. |
Also this pushes |
@ysbaddaden It currently does not use |
Hello @Qwerp-Derp, this is a neat idea and nice start for a PR! As commented above by other contributors, perhaps will be great to review the implementation to take the following in consideration:
I think it will be also possible to add specs for first and last points, which will be a great addition. Looking forward your updates and once again, thank you for your contribution and interest in Crystal! |
src/exception.cr
Outdated
if message.nil? | ||
"\e[1m\e[31m" + self.class.name + "\e[0m" | ||
else | ||
"\e[1m\e[31m" + self.class.name + ": \e[0m\e[1m" + message.not_nil! + "\e[0m" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of building up strings with +
, this method should probably take the io object as a parameter and use <<
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed with new commit!
src/exception.cr
Outdated
def inspect_with_backtrace(io : IO) | ||
io << message << " (" << self.class << ")\n" | ||
trace = {% if flag?(:raw_err) %} | ||
self.class.name + (message.nil? ? "" : ": #{message}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here, you should probably use IO#<<
@straight-shoota You mean |
I have a plan to separate |
@Qwerp-Derp No, I mean, that an application can implement a It might also make sense to override the default behaviour to always include ANSI codes, even when not writing to a tty. So the configuration needs the options |
are you guys really considering adding colored output to all runtime exceptions? i've never seen that in any other language. it surprises me that you are even considering that you want colored output? do it in your code. this doesn't belong in the standard library |
@larubujo Elm does it, and their error messages are considered some of the best in the industry. |
@Qwerp-Derp Do you have screenshots on both black backgrounds and non-black backgrounds? |
@Fryguy Elm does it? are you talking about compile-time errors or runtime errors? can you show an example? |
@larubujo http://elm-lang.org/blog/compiler-errors-for-humans#colors-and-formatting . For an example, just make a syntax error in one of the online examples (e.g. http://elm-lang.org/examples/hello-html) and hit Compile. |
@Fryguy thats my point. thats a compile-time error. and crystal shows colors in compile-time errors (for example did you mean and other things). but runtime errors are a totally different thing. no language shows colors in there. what if you put that in a file. thats plain wrong. runtime errors in elm probably happen in the browser, so no colors there. |
@larubujo What would speak against the idea except (almost) "nobody else does it"? As I have mentioned before, it will need some sane defaults checking if it makes sense to colorize. Writing to a file is no tty, so it shouldn't be colored. |
Ah thanks for the clarification @larubujo... I didn't realize you were making a distinction between compile-time versus runtime and that one should have it but not the other...I thought you meant neither should have it. It's an interesting point. |
@straight-shoota what benefit do colors add for runtime errors? what if i dont like the colors? and you are talking about configuring this in all apps. increasing complexity. definitely not something for the standard lib. maybe no other language does it because its just plain wrong. and useless. |
Can you explain why you think runtime errors are different from compile-time ones regarding colourization? |
For me colorized runtime errors look a bit weird, maybe we can make it optional, depending on programmer preference: require "colorize"
require "option_parser"
OptionParser.parse! do |parser|
parser.banner = "Usage: salute [arguments]"
parser.on("-c", "--color", "Colorize error output") do
Colorize.errors = true
end
end Then:
|
@faustinoaq If at all, that property should live in Besides the configuration checking if it's outputting to a TTY and the (theoretical) In this scenario, the chain would be:
|
Sigh. How to turn a simple feature to help development into a configuration hell. Let's get back to the core idea: help the developer. Nothing more. If the output is a TTY and it's not a release build then enable colored backtraces. Period. There are no need for further configuration, especially at runtime, especially for production builds. |
I vote to forget it. Adding colours to the stacktrace adds very little and there will always be people who dislike it and want to turn it off. |
@RX14, so is this request getting cancelled? Also, sorry everyone for being inactive recently - I was busy with personal things. |
@Qwerp-Derp I don't make decisions for crystal, we as a team make decisions. I can't say if we'll close this issue or not. I vote against it, if the rest of the core team wants it it'll probably get in. But yes, I think it's much more trouble than it's worth. |
@Qwerp-Derp I don't think Colorize Exceptions must be default on core crystal but maybe a shard, WDYT? Would be possible to convert this PR into a shard? 😅 |
How do I check to see if the |
@Qwerp-Derp use Lines 89 to 91 in 007899a
|
@hugoabonizio Thanks for the help! I included it in my recent commit. |
@faustinoaq I personally don't think this would work as a shard, since this requires a close connection to the compiler and error messages itself. If the Crystal team doesn't approve of this, I might consider changing this into a shard, but it would require more effort since I don't have direct access to the compiler. |
src/exception.cr
Outdated
@@ -21,6 +21,11 @@ class Exception | |||
property callstack : CallStack? | |||
|
|||
def initialize(@message : String? = nil, @cause : Exception? = nil) | |||
{% if flag?(:release) %} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ooops, this will get re-initialized on every Exception.new
.
This might be moar up your alley:
class_getter colorize : Bool = {% if flag?(:release) %} STDERR.tty? {% else %} false {% end %}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, that's neat. Will change now!
@RX14 Just curious, who's in the Crystal team? I know asterite and bcardiff are in it (I don't think I should tag them just yet). Anyone else? |
@Qwerp-Derp This feature does not touch the compiler, all your edits are in the standard library. Through reopening and monkey-patching this could easily be implemented as a shard, but these practices should obviously be avoided if possible. I think this would be an excellent feature for the standard library and I think it can be implemented in a way that it is non-intrusive but still configurable without incinerating configuration hell. |
Bump - any updates to this thread? |
I like the idea of using only bold, no colours. |
@RX14 The problem I can see with that is bold text doesn't add that much to distinguish between it and normal text - the whole point of the PR is to improve readability by a large amount, otherwise there wouldn't be much need for the PR at all. Adding colours would allow core parts of the error to be distinguished at a glance. |
I don't really get it why you would prefer "only bold" over coloured output. Colour has a some clear benefits. It doesn't just emphasise text (which can work better or less depending on the font face) but also conveys meaning, a colour encodes a certain message (red = error, danger), It is possible to highlight different aspects with different colours. Of course, it shouldn't be painted in vivid colours, but a few accents would be a great improvement. It is true that some people have difficulties with colour perception. Tis usually doesn't mean to see no color at all but to have problems differentiating colours. In the worst case, this would not be worse than the status quo. For everyone else, colours are clearly an improvement because it structures and highlights the output. |
For this code: def foo
[1][2]
end
foo I get:
First line is the error message and exception. There's just that. I don't think adding colors, or even bold text, improves the way you are going to perceive or handle that error. The compiler uses bold text for some stuff, but it could simply just not do that. The few cases where color is used is to highlight pieces of code with squiggly marks, and yellow text for suggestions (did you mean?). Here it's just two pieces of information. So, this added complexity is not worth it. Plus, this can be really easily implemented as a shard: just redefine |
@asterite I do really wish that the symbol names were first and the file names were second though, or there was some kind of alignment. The symbol names are nowhere near each other in terms of line spacing, because the file names are so variable in length. |
It's easy: we put them on a separate line with an extra indentation... Either the name or the location. I think both Go and Python do this (can't remember now). |
Bump (since I saw this PR still existed). I might as well close this PR for now and reopen this as an issue, since Crystal has changed a lot in two(!) years and I'll probably need to do a complete rewrite. Plus, the proposal itself is pretty split, from what I've gathered in the comments. |
Extra fixes to #4968.
A few problems with the PR so far:
callstack_spec.cr
doesn't pass right now, for obvious reasons. I'll edit the spec files once this is finalised.callstack.cr
might not be to everyone's liking - I personally am fine with this, and the colours are more like placeholders to highlight the important parts of the error.exception.cr
to have it likeIndexError: Index out of bounds
. I'm not sure if this is good or not.