#!/usr/bin/env python3 import argparse import json import os import re from urllib.parse import urlsplit def load_state(filename): try: with open(filename, 'r') as f: return json.load(f) except FileNotFoundError: return {} def save_state(filename, state): with open(filename, 'w') as f: json.dump(state, f) def lint_file(filename): issues = [] with open(filename, 'r') as f: lines = f.readlines() for i, line in enumerate(lines): # Check for trailing whitespace if line.rstrip() != line: issues.append(f"Trailing whitespace on line {i+1}") # Check for missing alt text in images if re.search(r'!\[(.*?)\]\((.*?)\)', line): match = re.search(r'!\[(.*?)\]\((.*?)\)', line) alt_text = match.group(1) if not alt_text: issues.append(f"Missing alt text on line {i+1}") # Check for broken links if re.search(r'\[(.*?)\]\((.*?)\)', line): match = re.search(r'\[(.*?)\]\((.*?)\)', line) link = match.group(2) if not urlsplit(link).scheme: issues.append(f"Broken link on line {i+1}: {link}") return issues def lint_headings(lines): issues = [] heading_styles = set() for i, line in enumerate(lines): if re.search(r'^#{1,6} .*$', line): match = re.search(r'^#{1,6} .*$', line) heading_level = match.group(0).find(' ') - 1 heading_styles.add(heading_level) if len(heading_styles) > 1: issues.append("Inconsistent heading styles") return issues def main(): parser = argparse.ArgumentParser(description='A markdown linter') subparsers = parser.add_subparsers(dest='command') lint_parser = subparsers.add_parser('lint', help='Lint a markdown file') lint_parser.add_argument('filename', help='Markdown file to lint') state_parser = subparsers.add_parser('state', help='Show linting state') args = parser.parse_args() if args.command == 'lint': issues = lint_file(args.filename) with open(args.filename, 'r') as f: lines = f.readlines() heading_issues = lint_headings(lines) issues.extend(heading_issues) if issues: print("\033[91mIssues found:\033[0m") for issue in issues: print(f"\033[91m* {issue}\033[0m") else: print("\033[92mNo issues found\033[0m") elif args.command == 'state': state = load_state('marklint-state.json') print(state) else: parser.print_help() if __name__ == '__main__': main()