857587c1 [studio] Cleanup eb3d53c9 [studio] Cleanup 14d58f3f [studio] Fix Navigation shortcuts for non-Mac systems 5f239790 [studio,nostalgia/gfx/studio/tilesheet] Fix copy/cut/paste enablement when there is no selection 58e0ecb4 [studio] Make FilePickerPopup accept on double click of a file 8838bf42 [studio] Fix to properly copy file that has the same name as deleted file bddc544d [nostalgia] Update release notes a9437191 [studio,turbine] Add support for mouse back/forward buttons 9d8da7cc [ox/std] Make strToInt return error for empty string 394b568e [studio] Add Back/Forward navigation 78e9f70d [nostalgia] Update release notes 12e5623f [ox/logconn] Add exception handling for logger thread cfdfb0a8 [studio] Fix file deletion to close file even if not active 56e66530 [studio] Cleanup 7415ce4b [nostalgia/gfx/studio] Cleanup 05f42150 [olympic] Add new loc command to Makefile 8ea2bc69 [nostalgia] Update release notes c7809241 [studio] Add [DEBUG] tag to About in debug builds 8c538560 [nostalgia/gfx/studio/palette] Make RGB key shortcuts work when color channel inputs are focused c3e75bdb [nostalgia/gfx/studio/tilesheet] Cleanup git-subtree-dir: deps/nostalgia git-subtree-split: 857587c18b4695eacd31457e3c30b4971b4e46e8
60 lines
1.8 KiB
Python
Executable File
60 lines
1.8 KiB
Python
Executable File
#! /usr/bin/env python3
|
|
|
|
from pathlib import Path
|
|
import argparse
|
|
|
|
def parse_args():
|
|
parser = argparse.ArgumentParser(description="Count and sort lines of code in selected files.")
|
|
parser.add_argument(
|
|
"--search-dirs", nargs="+", required=True,
|
|
help="List of directories to search (recursively)."
|
|
)
|
|
parser.add_argument(
|
|
"--include-exts", nargs="+", required=True,
|
|
help="List of file extensions to include (e.g. .cpp .hpp .py)"
|
|
)
|
|
parser.add_argument(
|
|
"--exclude-paths", nargs="*", default=[],
|
|
help="Full file paths to exclude from counting."
|
|
)
|
|
return parser.parse_args()
|
|
|
|
|
|
def main():
|
|
args = parse_args()
|
|
include_exts = tuple(args.include_exts)
|
|
exclude_paths = {Path(p).resolve() for p in args.exclude_paths}
|
|
files = []
|
|
for base in args.search_dirs:
|
|
path = Path(base)
|
|
if path.is_dir():
|
|
for file in path.rglob("*"):
|
|
try:
|
|
resolved_file = file.resolve()
|
|
if (
|
|
file.is_file()
|
|
and file.name.endswith(include_exts)
|
|
and resolved_file not in exclude_paths
|
|
):
|
|
files.append(str(resolved_file))
|
|
except FileNotFoundError:
|
|
continue
|
|
line_counts = []
|
|
total_lines = 0
|
|
for f in files:
|
|
try:
|
|
with open(f, "r", encoding="utf-8", errors="ignore") as file:
|
|
lines = sum(1 for _ in file)
|
|
line_counts.append((lines, f))
|
|
total_lines += lines
|
|
except Exception as e:
|
|
print(f"Failed to read {f}: {e}")
|
|
line_counts.sort()
|
|
for count, filename in line_counts:
|
|
print(f"{count:>7} {filename}")
|
|
print(f"{total_lines:>7} total")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|