Files
troupe-standup/wizard-work/spark/todo_finder.py
T

73 lines
2.8 KiB
Python
Executable File

#!/usr/bin/env python3
"""Quick utility to find TODO/FIXME comments in code files."""
import os
import re
import sys
from pathlib import Path
from collections import defaultdict
from typing import Dict, List, Tuple, Set
# Match TODO/FIXME/XXX/HACK as comment markers
TODO_PATTERNS = {
'TODO': re.compile(r'#\s*TODO[:\s]*(.*?)(?:\n|$)', re.IGNORECASE),
'TODO.md': re.compile(r'#\s*TODO\.md[:\s]*(.*?)(?:\n|$)', re.IGNORECASE),
'FIXME': re.compile(r'#\s*FIXME[:\s]*(.*?)(?:\n|$)', re.IGNORECASE),
'XXX': re.compile(r'#\s*XXX[:\s]*(.*?)(?:\n|$)', re.IGNORECASE),
'HACK': re.compile(r'#\s*HACK[:\s]*(.*?)(?:\n|$)', re.IGNORECASE),
}
def find_todos(directory: str, file_patterns: List[str] = None) -> Dict[str, List[Tuple[str, int, str]]]:
"""Find TODO/FIXME comments in files."""
if file_patterns is None:
file_patterns = ['*.py', '*.js', '*.ts', '*.md', '*.txt', '*.sh', '*.go', '*.rs', '*.c', '*.cpp', '*.h', '*.java', '*.rb', '*.rs']
results = defaultdict(list)
root_path = Path(directory)
# If path is a file, search just that file
if root_path.is_file():
files_to_search = [root_path]
else:
# If path is a directory, search for matching files
files_to_search = list(root_path.rglob('*'))
files_to_search = [f for f in files_to_search if f.is_file() and any(f.match(p) for p in file_patterns)]
for filepath in files_to_search:
try:
with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
for line_num, line in enumerate(f, 1):
for name, regex in TODO_PATTERNS.items():
match = regex.search(line)
if match:
comment = match.group(1).strip() if match.lastindex else ''
base_dir = root_path if root_path.is_dir() else root_path.parent
results[name].append((str(filepath), line_num, comment))
except Exception:
continue
return dict(results)
def main():
directory = sys.argv[1] if len(sys.argv) > 1 else '.'
print(f"Searching for TODOs in: {directory}")
print("=" * 60)
todos = find_todos(directory)
total = sum(len(v) for v in todos.values())
print(f"\nFound {total} item(s) across {len(todos)} pattern type(s)\n")
for pattern, items in sorted(todos.items()):
if items:
print(f"\n{pattern.upper()}:")
print("-" * 40)
for filepath, line_num, comment in items:
rel_path = os.path.relpath(filepath, directory if Path(directory).is_dir() else Path(directory).parent)
display = f"{rel_path}:{line_num}"
print(f" {display}")
if comment:
print(f" {comment}")
if __name__ == '__main__':
main()