-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Show code snippet on syntax and token missing errors #11332
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
Show code snippet on syntax and token missing errors #11332
Conversation
Overall the approach looks good to me. I would do the following changes:
Another option is to send the whole file to the exception and do all of the work in computing the snippet in the Exception.message/1 callback. But I am not sure if it would be better or worse. Thoughts? |
Agree 100% with 1 and 2! Instead of sending the whole file to the exception, I was thinking about sending it to parse_error and doing the snippet part there. Then I could send the snippet to the exception. Do you think it is okay? |
Yeah. |
There are some places where the error line is available, but not the column. How should these be handled? As of now (pushing here soon), I'm printing the line, but not the column indicator. Looks a bit weird, maybe nothing should be printed. |
Can you share those cases? I would be more inclined to not show anything. |
Btw, once this is done and merged, I will gladly accept PRs for EEx and HEEx in LiveView too. |
Strings that go through the error flow without a column, extracted from the test suite:
|
I see. All syntax error coming from the parser. :( let me play with this a bit because Erlang has to have addressed them somehow. |
Please rebase, we should have the column everywhere now. But keep in mind that it can be turned off, so it is still important to check if the column is there (and skip the snippet if the column is not there). |
5e92ea4
to
7710441
Compare
fa9aaaa
to
487ff7c
Compare
Okay, there's a few more things to refactor (mainly move part of the logic to exception, also writing tests), but I think it works for real now. The one thing that I didn't like was adding 2 (!) new arguments to parse_error. I think the main thing that is left to handle is the formatting of the message. Currently it looks like this:
The first message implementation is really naive and doesn't handle most of the stuff. When we have a decision on how the message should look like, I can reimplement it. My first suggestion is:
So it would look something like:
No indentation case:
Many digits case:
|
dbb65c5
to
8be83a6
Compare
7204872
to
46872ea
Compare
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.
Looks great! Just one minor comment on parse_error
API. I understand the goal is to mirror location, but location was designed to mirror the meta node and I don't think we need to replicate it here. :)
lib/elixir/src/elixir_errors.erl
Outdated
{column, Column} -> | ||
Lines = string:split(InputString, "\n", all), | ||
Snippet = elixir_utils:characters_to_binary(lists:nth(Line - StartLine + 1, Lines)), | ||
#{content => Snippet, column => (Column - StartColumn + 1)}; |
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.
I think you need to subtract start_column only if the line is the same as the start_line.
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, columns start at 0, so we don't need the + 1
. It only works because you do column - 1
in format_snippet
. :)
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.
Here are two tests that you can add:
Code.string_to_quoted!("2 + * 3", column: 3)
Code.string_to_quoted!(":ok\n2 + * 3", column: 3)
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, what if we rename column
to something else to avoid confusion? Maybe offset
?
Co-authored-by: José Valim <jose.valim@gmail.com>
a21683f
to
5b8773e
Compare
Doctest did not compile, got: (TokenMissingError) test/ex_unit/doc_test_test.exs:#{starting_line + 69}:7: missing terminator: } (for "{" starting at line #{starting_line + 69}) | ||
#{line_placeholder(starting_line + 69)} | | ||
#{starting_line + 69} | {:ok, #MapSet<[1, 2, 3]>} | ||
#{line_placeholder(starting_line + 69)} | ^. If you are planning to assert on the result of an iex> expression which contains a value inspected as #Name<...>, please make sure the inspected value is placed at the beginning of the expression; otherwise Elixir will treat it as a comment due to the leading sign #. |
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.
Perhaps we should add a newline by the beginning of this hint?
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.
Yes, we should definitely.
💚 💙 💜 💛 ❤️ |
Closes #11280.
Mostly works, there are 3~4 tests that legitimately fail.
Need to carefully refactor as I've been smashing keyboard until it works. 🥲
If possible, some directions would help me: is the approach that I took okay? Is there anything big that must be done differently?