From 820fa1760d678cec831f0612e2e39049388b2f18 Mon Sep 17 00:00:00 2001 From: Antoine Viallon Date: Mon, 8 May 2023 17:29:03 +0200 Subject: [PATCH] source: greatly improve location reporting for multi-line source extracts --- compiler/source.py | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/compiler/source.py b/compiler/source.py index 6026807..ce6b79a 100644 --- a/compiler/source.py +++ b/compiler/source.py @@ -1,8 +1,11 @@ from __future__ import annotations +import math +from dataclasses import dataclass + from beartype import beartype from beartype.typing import Optional -from dataclasses import dataclass + @beartype @dataclass @@ -44,21 +47,35 @@ class SourceLocation: source_lines[-1] = source_lines[-1][:self.end.character] return "\n".join(source_lines) - def show_in_source(self) -> str: - source = self.source.splitlines(keepends=False) - source_line = source[self.begin.line] - result = [source_line] - if self.begin.line != self.end.line: - return "\n".join(result) - - line = " " * self.begin.character - line += "^" + "-" * max(0, (self.end.character - self.begin.character - 1)) - line += " " * (len(source_line) - len(line)) + @staticmethod + def underline(string: str, begin: int, end: int) -> str: + assert begin <= end + result = [string] + line = " " * begin + line += "^" + "~" * max(0, (end - begin - 1)) + line += " " * (len(string) - len(line)) result += [line] return "\n".join(result) + def show_in_source(self) -> str: + source = self.source.splitlines(keepends=False) + line_number_maxlen = int(math.log10(len(source)) + 1) + lines = source[self.begin.line:self.end.line + 1] + result = [] + begin_char = self.begin.character + while len(lines) > 0: + line = lines.pop(0) + end_char = self.end.character if len(lines) == 0 else len(line) + result += [SourceLocation.underline(line, begin_char, end_char)] + begin_char = 0 + for i, source_line in enumerate(result): + line_prefix = f"Line {self.begin.line + i:0>{line_number_maxlen}}: " + lines = source_line.splitlines(keepends=False) + lines[0] = line_prefix + lines[0] + lines[1] = " " * len(line_prefix) + lines[1] + result[i] = "\n".join(lines) - + return "\n".join(result)