feat(cli): add option to highlight diagnostics
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -5,4 +5,5 @@ venv
|
|||||||
.venv
|
.venv
|
||||||
*.pyc
|
*.pyc
|
||||||
uv.lock
|
uv.lock
|
||||||
.python-version
|
.python-version
|
||||||
|
/out
|
||||||
@@ -7,6 +7,7 @@ from typing import Generic, Optional, Protocol, TextIO, TypeVar
|
|||||||
import midas.ast.midas as m
|
import midas.ast.midas as m
|
||||||
import midas.ast.python as p
|
import midas.ast.python as p
|
||||||
from midas.ast.location import Location
|
from midas.ast.location import Location
|
||||||
|
from midas.checker.diagnostic import Diagnostic
|
||||||
|
|
||||||
H = TypeVar("H", bound="Highlighter", contravariant=True)
|
H = TypeVar("H", bound="Highlighter", contravariant=True)
|
||||||
|
|
||||||
@@ -71,6 +72,7 @@ class Highlighter(ABC):
|
|||||||
openings: list[str] = self.openings.get(pos, [])
|
openings: list[str] = self.openings.get(pos, [])
|
||||||
line_buf += "".join(closings + openings)
|
line_buf += "".join(closings + openings)
|
||||||
line_buf += char
|
line_buf += char
|
||||||
|
line_buf += "".join(self.closings.get((lineno, len(line)), []))
|
||||||
line_buf += "</div></div>"
|
line_buf += "</div></div>"
|
||||||
lines.append(" " + line_buf)
|
lines.append(" " + line_buf)
|
||||||
lines.extend(
|
lines.extend(
|
||||||
@@ -83,7 +85,7 @@ class Highlighter(ABC):
|
|||||||
|
|
||||||
buf.write("\n".join(lines))
|
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:
|
if node.location is None:
|
||||||
return
|
return
|
||||||
if node.location.end_lineno is None or node.location.end_col_offset is None:
|
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}">'
|
opening: str = f'<span class="{cls}" title="{cls}">'
|
||||||
closing: str = "</span>"
|
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.openings.setdefault(start_pos, []).append(opening)
|
||||||
self.closings.setdefault(end_pos, []).insert(0, closing)
|
self.closings.setdefault(end_pos, []).insert(0, closing)
|
||||||
if start_pos[0] != end_pos[0]:
|
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")
|
self.wrap(expr, "type")
|
||||||
if expr.template is not None:
|
if expr.template is not None:
|
||||||
expr.template.accept(self)
|
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)
|
||||||
|
|||||||
32
midas/cli/hl_diagnostic.css
Normal file
32
midas/cli/hl_diagnostic.css
Normal 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%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ from midas.ast.location import Location
|
|||||||
from midas.ast.printer import MidasAstPrinter, PythonAstPrinter
|
from midas.ast.printer import MidasAstPrinter, PythonAstPrinter
|
||||||
from midas.checker.checker import Checker
|
from midas.checker.checker import Checker
|
||||||
from midas.checker.diagnostic import Diagnostic
|
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.midas import MidasLexer
|
||||||
from midas.lexer.token import Token, TokenType
|
from midas.lexer.token import Token, TokenType
|
||||||
from midas.parser.midas import MidasParser
|
from midas.parser.midas import MidasParser
|
||||||
@@ -28,8 +28,9 @@ def midas():
|
|||||||
|
|
||||||
|
|
||||||
@midas.command()
|
@midas.command()
|
||||||
|
@click.option("-l", "--highlight", type=click.File("w"))
|
||||||
@click.argument("file", type=click.File("r"))
|
@click.argument("file", type=click.File("r"))
|
||||||
def compile(file: TextIO):
|
def compile(highlight: Optional[TextIO], file: TextIO):
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
source: str = file.read()
|
source: str = file.read()
|
||||||
tree: ast.Module = ast.parse(source, filename=file.name)
|
tree: ast.Module = ast.parse(source, filename=file.name)
|
||||||
@@ -50,6 +51,10 @@ def compile(file: TextIO):
|
|||||||
indent=4,
|
indent=4,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
if highlight is not None:
|
||||||
|
highlighter = DiagnosticsHighlighter(source)
|
||||||
|
highlighter.highlight(diagnostics)
|
||||||
|
highlighter.dump(highlight)
|
||||||
|
|
||||||
|
|
||||||
@midas.group()
|
@midas.group()
|
||||||
|
|||||||
Reference in New Issue
Block a user