73 lines
2.8 KiB
Python
Executable File
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() |