diff --git a/.gitignore b/.gitignore index b540ed8..66f4aa9 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ venv .venv *.pyc uv.lock -.python-version \ No newline at end of file +.python-version +/out \ No newline at end of file diff --git a/midas/cli/highlighter.py b/midas/cli/highlighter.py index 9b24259..b297404 100644 --- a/midas/cli/highlighter.py +++ b/midas/cli/highlighter.py @@ -7,6 +7,7 @@ from typing import Generic, Optional, Protocol, TextIO, TypeVar import midas.ast.midas as m import midas.ast.python as p from midas.ast.location import Location +from midas.checker.diagnostic import Diagnostic H = TypeVar("H", bound="Highlighter", contravariant=True) @@ -71,6 +72,7 @@ class Highlighter(ABC): openings: list[str] = self.openings.get(pos, []) line_buf += "".join(closings + openings) line_buf += char + line_buf += "".join(self.closings.get((lineno, len(line)), [])) line_buf += "" lines.append(" " + line_buf) lines.extend( @@ -83,7 +85,7 @@ class Highlighter(ABC): buf.write("\n".join(lines)) - def wrap(self, node: Locatable, cls: str): + def wrap(self, node: Locatable, cls: str, message: Optional[str] = None): if node.location is None: return if node.location.end_lineno is None or node.location.end_col_offset is None: @@ -95,6 +97,10 @@ class Highlighter(ABC): ) opening: str = f'' closing: str = "" + if message is not None: + opening = f'{opening}' + closing = f'{closing}{message}' + self.openings.setdefault(start_pos, []).append(opening) self.closings.setdefault(end_pos, []).insert(0, closing) if start_pos[0] != end_pos[0]: @@ -260,3 +266,11 @@ class MidasHighlighter(Highlighter, m.Stmt.Visitor[None], m.Expr.Visitor[None]): self.wrap(expr, "type") if expr.template is not None: expr.template.accept(self) + + +class DiagnosticsHighlighter(Highlighter): + EXTRA_CSS_PATH: Optional[Path] = Path(__file__).parent / "hl_diagnostic.css" + + def highlight(self, diagnostics: list[Diagnostic]): + for diagnostic in diagnostics: + self.wrap(diagnostic, str(diagnostic.type).lower(), diagnostic.message) diff --git a/midas/cli/hl_diagnostic.css b/midas/cli/hl_diagnostic.css new file mode 100644 index 0000000..f79bab5 --- /dev/null +++ b/midas/cli/hl_diagnostic.css @@ -0,0 +1,32 @@ +span { + &.error { + --col: 255, 0, 0; + } + &.warning { + --col: 250, 160, 0; + } + &.info { + --col: 150, 190, 250; + } + + &.with-msg { + position: relative; + &:not(:hover) { + .message { + display: none; + } + } + + .message { + position: absolute; + top: calc(100% + 0.2em); + left: -.2em; + background-color: black; + color: white; + padding: 0.2em 0.4em; + border-radius: .2em; + z-index: 10; + width: 300%; + } + } +} \ No newline at end of file diff --git a/midas/cli/main.py b/midas/cli/main.py index 9119a07..220cca9 100644 --- a/midas/cli/main.py +++ b/midas/cli/main.py @@ -13,7 +13,7 @@ from midas.ast.location import Location from midas.ast.printer import MidasAstPrinter, PythonAstPrinter from midas.checker.checker import Checker from midas.checker.diagnostic import Diagnostic -from midas.cli.highlighter import Highlighter, MidasHighlighter, PythonHighlighter +from midas.cli.highlighter import DiagnosticsHighlighter, Highlighter, MidasHighlighter, PythonHighlighter from midas.lexer.midas import MidasLexer from midas.lexer.token import Token, TokenType from midas.parser.midas import MidasParser @@ -28,8 +28,9 @@ def midas(): @midas.command() +@click.option("-l", "--highlight", type=click.File("w")) @click.argument("file", type=click.File("r")) -def compile(file: TextIO): +def compile(highlight: Optional[TextIO], file: TextIO): logging.basicConfig(level=logging.DEBUG) source: str = file.read() tree: ast.Module = ast.parse(source, filename=file.name) @@ -50,6 +51,10 @@ def compile(file: TextIO): indent=4, ) ) + if highlight is not None: + highlighter = DiagnosticsHighlighter(source) + highlighter.highlight(diagnostics) + highlighter.dump(highlight) @midas.group()