base.py 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. from abc import ABC, abstractmethod
  2. from pydantic import BaseModel
  3. class LintResult(BaseModel):
  4. file: str
  5. line: int # 1-indexed
  6. column: int # 1-indexed
  7. message: str
  8. def visualize(self, half_window: int = 3) -> str:
  9. """Visualize the lint result by print out all the lines where the lint result is found.
  10. Args:
  11. half_window: The number of context lines to display around the error on each side.
  12. """
  13. with open(self.file, 'r') as f:
  14. file_lines = f.readlines()
  15. # Add line numbers
  16. _span_size = len(str(len(file_lines)))
  17. file_lines = [
  18. f'{i + 1:>{_span_size}}|{line.rstrip()}'
  19. for i, line in enumerate(file_lines)
  20. ]
  21. # Get the window of lines to display
  22. assert self.line <= len(file_lines) and self.line > 0
  23. line_idx = self.line - 1
  24. begin_window = max(0, line_idx - half_window)
  25. end_window = min(len(file_lines), line_idx + half_window + 1)
  26. selected_lines = file_lines[begin_window:end_window]
  27. line_idx_in_window = line_idx - begin_window
  28. # Add character hint
  29. _character_hint = (
  30. _span_size * ' '
  31. + ' ' * (self.column)
  32. + '^'
  33. + ' ERROR HERE: '
  34. + self.message
  35. )
  36. selected_lines[line_idx_in_window] = (
  37. f'\033[91m{selected_lines[line_idx_in_window]}\033[0m'
  38. + '\n'
  39. + _character_hint
  40. )
  41. return '\n'.join(selected_lines)
  42. class LinterException(Exception):
  43. """Base class for all linter exceptions."""
  44. pass
  45. class BaseLinter(ABC):
  46. """Base class for all linters.
  47. Each linter should be able to lint files of a specific type and return a list of (parsed) lint results.
  48. """
  49. encoding: str = 'utf-8'
  50. @property
  51. @abstractmethod
  52. def supported_extensions(self) -> list[str]:
  53. """The file extensions that this linter supports, such as .py or .tsx."""
  54. return []
  55. @abstractmethod
  56. def lint(self, file_path: str) -> list[LintResult]:
  57. """Lint the given file.
  58. file_path: The path to the file to lint. Required to be absolute.
  59. """
  60. pass