Skip to content

Commit

Permalink
updated tests, new implementation plan, all tests green
Browse files Browse the repository at this point in the history
  • Loading branch information
rdmueller committed Dec 20, 2024
1 parent fa3c6f4 commit f76c03f
Show file tree
Hide file tree
Showing 39 changed files with 1,559 additions and 328 deletions.
Binary file removed .coverage
Binary file not shown.
134 changes: 66 additions & 68 deletions asciidoc_linter/reporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,96 +3,94 @@
Different output formatters for lint results
"""

from dataclasses import dataclass
from typing import List, Optional
import json
from abc import ABC, abstractmethod
from typing import List
from .rules import Finding

class Reporter(ABC):
"""Base class for all reporters"""

@abstractmethod
def report(self, findings: List[Finding]) -> str:
"""Format and return the findings"""
pass
@dataclass
class LintError:
"""Represents a single lint error"""
file: Optional[str]
line: int
message: str

@dataclass
class LintReport:
"""Contains all lint errors for a document"""
errors: List[LintError]

def __bool__(self):
return bool(self.errors)

class ConsoleReporter(Reporter):
"""Reports findings to the console in a human-readable format"""
def __len__(self):
return len(self.errors)

class Reporter:
"""Base class for formatting lint reports"""

def report(self, findings: List[Finding]) -> str:
def format_report(self, report: LintReport) -> str:
"""Format the report as string"""
output = []
for finding in findings:
severity = finding.severity.value.upper()
location = f"line {finding.position.line}"
if finding.position.column:
location += f", column {finding.position.column}"
for error in report.errors:
location = f"line {error.line}"
if error.file:
location = f"{error.file}:{location}"

output.append(
f"{severity}: {finding.message} ({finding.rule_id})"
f"\n at {location}"
)
if finding.context:
output.append(f" context: {finding.context}")
output.append(f"{location}: {error.message}")

return "\n".join(output)

class JsonReporter(Reporter):
"""Reports findings in JSON format"""

def report(self, findings: List[Finding]) -> str:
def format_report(self, report: LintReport) -> str:
return json.dumps([
{
'rule_id': f.rule_id,
'message': f.message,
'severity': f.severity.value,
'position': {
'line': f.position.line,
'column': f.position.column
},
'context': f.context
'file': error.file,
'line': error.line,
'message': error.message
}
for f in findings
for error in report.errors
], indent=2)

class HtmlReporter(Reporter):
"""Reports findings in HTML format"""

def report(self, findings: List[Finding]) -> str:
def format_report(self, report: LintReport) -> str:
rows = []
for f in findings:
severity_class = f"severity-{f.severity.value}"
for error in report.errors:
location = f"Line {error.line}"
if error.file:
location = f"{error.file}:{location}"

rows.append(
f'<tr class="{severity_class}">'
f'<td>{f.severity.value.upper()}</td>'
f'<td>{f.rule_id}</td>'
f'<td>{f.message}</td>'
f'<td>Line {f.position.line}</td>'
f'<tr>'
f'<td>{location}</td>'
f'<td>{error.message}</td>'
f'</tr>'
)

return f"""
<html>
<head>
<style>
table {{ border-collapse: collapse; width: 100%; }}
th, td {{ padding: 8px; text-align: left; border: 1px solid #ddd; }}
th {{ background-color: #f2f2f2; }}
.severity-error {{ background-color: #ffe6e6; }}
.severity-warning {{ background-color: #fff3e6; }}
.severity-info {{ background-color: #e6f3ff; }}
</style>
</head>
<body>
<h1>AsciiDoc Lint Results</h1>
<table>
<tr>
<th>Severity</th>
<th>Rule</th>
<th>Message</th>
<th>Location</th>
</tr>
{"".join(rows)}
</table>
</body>
</html>
"""
return f"""<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>AsciiDoc Lint Results</title>
<style>
table {{ border-collapse: collapse; width: 100%; }}
th, td {{ padding: 8px; text-align: left; border: 1px solid #ddd; }}
th {{ background-color: #f2f2f2; }}
tr:nth-child(even) {{ background-color: #f9f9f9; }}
tr:hover {{ background-color: #f5f5f5; }}
</style>
</head>
<body>
<h1>AsciiDoc Lint Results</h1>
<table>
<tr>
<th>Location</th>
<th>Message</th>
</tr>
{"".join(rows)}
</table>
</body>
</html>"""
56 changes: 54 additions & 2 deletions asciidoc_linter/rules/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,60 @@ class Rule:
severity: Severity = Severity.WARNING

def check_line(self, line: str, line_number: int, context: List[str]) -> List[Finding]:
"""Check a single line for rule violations"""
raise NotImplementedError()
"""
Check a single line for rule violations.
Args:
line: The current line to check
line_number: The line number in the document (0-based)
context: The complete document as a list of lines
Returns:
List of Finding objects representing rule violations
"""
findings = []

# Get previous and next lines if available
prev_line = context[line_number - 1] if line_number > 0 else None
next_line = context[line_number + 1] if line_number < len(context) - 1 else None

# Check the line content
line_findings = self.check_line_content(line, line_number)
if line_findings:
findings.extend(line_findings)

# Check line context if needed
context_findings = self.check_line_context(line, line_number, prev_line, next_line)
if context_findings:
findings.extend(context_findings)

return findings

def check_line_content(self, line: str, line_number: int) -> List[Finding]:
"""
Check the content of a single line.
Override this method in concrete rule implementations.
"""
return []

def check_line_context(self, line: str, line_number: int,
prev_line: Optional[str], next_line: Optional[str]) -> List[Finding]:
"""
Check a line in context with its surrounding lines.
Override this method in concrete rule implementations.
"""
return []

def create_finding(self, line_number: int, message: str,
column: Optional[int] = None, context: Optional[str] = None) -> Finding:
"""Helper method to create a Finding object"""
return Finding(
rule_id=self.id,
position=Position(line_number, column),
message=message,
severity=self.severity,
context=context
)

class RuleRegistry:
"""Registry for all available rules"""
Expand Down
2 changes: 1 addition & 1 deletion asciidoc_linter/rules/base.py.meta
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Base functionality for rules including Position class
Updated base.py with check_line implementation
Loading

0 comments on commit f76c03f

Please sign in to comment.