Skip to content

Commit

Permalink
fix: Fix rendering of line breaks in diagnostics (#819)
Browse files Browse the repository at this point in the history
Fixes #818
  • Loading branch information
mark-koch authored Feb 28, 2025
1 parent 8f90c04 commit 75efd22
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 12 deletions.
36 changes: 26 additions & 10 deletions guppylang/diagnostic.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,9 +233,8 @@ def render_diagnostic(self, diag: Diagnostic) -> None:
# Omit the title if we don't have a span, but a long message. This case
# should be fairly rare.
msg = diag.rendered_message or diag.rendered_title
self.buffer += textwrap.wrap(
f"{self.level_str(diag.level)}: {msg}",
self.MAX_MESSAGE_LINE_LEN,
self.buffer += wrap(
f"{self.level_str(diag.level)}: {msg}", self.MAX_MESSAGE_LINE_LEN
)
else:
span = to_span(diag.span)
Expand Down Expand Up @@ -263,16 +262,12 @@ def render_diagnostic(self, diag: Diagnostic) -> None:
)
if diag.rendered_message:
self.buffer.append("")
self.buffer += textwrap.wrap(
diag.rendered_message,
self.MAX_MESSAGE_LINE_LEN,
replace_whitespace=False, # Keep \n's in the message
)
self.buffer += wrap(diag.rendered_message, self.MAX_MESSAGE_LINE_LEN)
# Finally, render all sub-diagnostics that have a non-span message
for sub_diag in diag.children:
if sub_diag.rendered_message:
self.buffer.append("")
self.buffer += textwrap.wrap(
self.buffer += wrap(
f"{self.level_str(sub_diag.level)}: {sub_diag.rendered_message}",
self.MAX_MESSAGE_LINE_LEN,
)
Expand Down Expand Up @@ -368,7 +363,7 @@ def render_line(line: str, line_number: int | None = None) -> None:

# Render the label next to the highlight
if label:
[label_first, *label_rest] = textwrap.wrap(
[label_first, *label_rest] = wrap(
label,
self.MAX_LABEL_LINE_LEN,
# One space after the last `^`
Expand All @@ -386,3 +381,24 @@ def render_line(line: str, line_number: int | None = None) -> None:
def level_str(level: DiagnosticLevel) -> str:
"""Returns the text used to identify the different kinds of diagnostics."""
return level.name.lower().capitalize()


def wrap(text: str, width: int, **kwargs: Any) -> list[str]:
"""Custom version of `textwrap.wrap` that correctly handles text with line breaks.
Even with `replace_whitespace=False`, the original version doesn't count line breaks
as new paragraphs and instead would produce bad results like
```
short paragraph of text
long paragraph of text xxxxx # broken too early :(
xxxx xxxxxx xxxx xxxxx xxxxxxxx xxxxxxxxxxxx xxxxx xx
xxxx xxxxxxx xxxxx xx xxxx xxxxxxx xxxx
```
"""
return [
line
for paragraph in text.splitlines()
for line in (textwrap.wrap(paragraph, width, **kwargs) if paragraph else [""])
]
6 changes: 6 additions & 0 deletions tests/diagnostics/snapshots/test_message_new_line.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Error: Apple Apple Apple Apple Apple Apple Apple Apple Apple Apple Apple Apple
Apple Apple Apple Apple Apple Apple Apple Apple

Orange Orange Orange Orange Orange Orange Orange Orange Orange Orange Orange
Orange Orange Orange Orange Orange Orange Orange Orange Orange Orange Orange
Orange Orange Orange Orange Orange Orange Orange Orange
9 changes: 9 additions & 0 deletions tests/diagnostics/test_diagnostics_rendering.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,3 +212,12 @@ def test_three_line_span(snapshot, request):
span = Span(Loc(file, 1, 5), Loc(file, 3, 1))
diagnostic = MyError(span)
run_test(source, diagnostic, snapshot, request)


def test_message_new_line(snapshot, request):
@dataclass(frozen=True)
class MyError(Error):
message: ClassVar[str] = "Apple " * 20 + "\n\n" + "Orange " * 30

diagnostic = MyError(None)
run_test("", diagnostic, snapshot, request)
3 changes: 1 addition & 2 deletions tests/error/py_errors/python_err.err
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ Error: Python error (at $FILE:6:14)
Traceback printed below:

Traceback (most recent call last):
File "<string>",
line 1, in <module>
File "<string>", line 1, in <module>
ZeroDivisionError: division by zero

Guppy compilation failed due to 1 previous error

0 comments on commit 75efd22

Please sign in to comment.