122 lines
3.1 KiB
Python
122 lines
3.1 KiB
Python
import os
|
|
import glob
|
|
from typing import Dict, Any, List, Optional
|
|
|
|
|
|
def count_lines(file_path: str) -> int:
|
|
"""Count lines in a file."""
|
|
try:
|
|
with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
|
|
return len(f.readlines())
|
|
except Exception:
|
|
return 0
|
|
|
|
|
|
def get_language(file_path: str) -> str:
|
|
"""Determine language from file extension."""
|
|
ext = os.path.splitext(file_path)[1].lower()
|
|
language_map = {
|
|
".py": "Python",
|
|
".cs": "C#",
|
|
".js": "JavaScript",
|
|
".ts": "TypeScript",
|
|
".html": "HTML",
|
|
".css": "CSS",
|
|
".md": "Markdown",
|
|
".json": "JSON",
|
|
".xml": "XML",
|
|
".java": "Java",
|
|
".cpp": "C++",
|
|
".c": "C",
|
|
".h": "C/C++ Header",
|
|
".go": "Go",
|
|
".rs": "Rust",
|
|
".php": "PHP",
|
|
".rb": "Ruby",
|
|
".sh": "Shell",
|
|
".bat": "Batch",
|
|
".ps1": "PowerShell",
|
|
".sql": "SQL",
|
|
".yaml": "YAML",
|
|
".yml": "YAML",
|
|
}
|
|
return language_map.get(ext, "Other")
|
|
|
|
|
|
def analyze_code_stats(
|
|
root_dir: str = ".",
|
|
exclude_dirs: List[str] = None,
|
|
source_extensions: List[str] = None,
|
|
top_files_limit: int = 20,
|
|
) -> Dict[str, Any]:
|
|
"""Analyze code statistics for the project."""
|
|
if exclude_dirs is None:
|
|
exclude_dirs = ["node_modules", "venv", ".git", ".vs", "bin", "obj"]
|
|
|
|
if source_extensions is None:
|
|
source_extensions = [
|
|
".py",
|
|
".cs",
|
|
".js",
|
|
".ts",
|
|
".html",
|
|
".css",
|
|
".md",
|
|
]
|
|
|
|
stats = {
|
|
"total_lines": 0,
|
|
"file_count": 0,
|
|
"languages": {},
|
|
"top_files": [],
|
|
}
|
|
|
|
file_stats = []
|
|
|
|
# Convert exclude_dirs to absolute paths
|
|
exclude_paths = [os.path.join(root_dir, d) for d in exclude_dirs]
|
|
|
|
# Find all source files
|
|
for ext in source_extensions:
|
|
pattern = os.path.join(root_dir, "**", f"*{ext}")
|
|
for file_path in glob.glob(pattern, recursive=True):
|
|
# Skip excluded directories
|
|
if any(
|
|
file_path.startswith(exclude_path)
|
|
for exclude_path in exclude_paths
|
|
):
|
|
continue
|
|
|
|
# Count lines
|
|
lines = count_lines(file_path)
|
|
|
|
# Get language
|
|
language = get_language(file_path)
|
|
|
|
# Update statistics
|
|
stats["total_lines"] += lines
|
|
stats["file_count"] += 1
|
|
|
|
# Update language count
|
|
if language not in stats["languages"]:
|
|
stats["languages"][language] = 0
|
|
stats["languages"][language] += 1
|
|
|
|
# Add to file stats
|
|
file_stats.append(
|
|
{
|
|
"path": file_path,
|
|
"name": os.path.basename(file_path),
|
|
"lines": lines,
|
|
"language": language,
|
|
}
|
|
)
|
|
|
|
# Sort file stats by line count
|
|
file_stats.sort(key=lambda x: x["lines"], reverse=True)
|
|
|
|
# Get top files
|
|
stats["top_files"] = file_stats[:top_files_limit]
|
|
|
|
return stats
|