feat(cli): add option to highlight diagnostics

This commit is contained in:
2026-06-01 11:57:57 +02:00
parent 29f691e38a
commit 86ad348b99
4 changed files with 56 additions and 4 deletions

3
.gitignore vendored
View File

@@ -5,4 +5,5 @@ venv
.venv
*.pyc
uv.lock
.python-version
.python-version
/out

View File

@@ -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 += "</div></div>"
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'<span class="{cls}" title="{cls}">'
closing: str = "</span>"
if message is not None:
opening = f'<span class="with-msg">{opening}'
closing = f'{closing}<span class="message">{message}</span></span>'
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)

View File

@@ -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%;
}
}
}

View File

@@ -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()