feat: in-place per-file progress for classify, count, and large-file steps

This commit is contained in:
Jeff Smith 2026-04-06 14:26:37 -06:00
parent bbaf387cb7
commit 206d2d34f6
3 changed files with 49 additions and 10 deletions

View file

@ -3,8 +3,9 @@
import argparse import argparse
import json import json
import sys
import os import os
import shutil
import sys
from luminos_lib.tree import build_tree, render_tree from luminos_lib.tree import build_tree, render_tree
from luminos_lib.filetypes import classify_files, summarize_categories from luminos_lib.filetypes import classify_files, summarize_categories
@ -15,6 +16,28 @@ from luminos_lib.watch import watch_loop
from luminos_lib.report import format_report from luminos_lib.report import format_report
def _progress(label):
"""Return (on_file, finish) for in-place per-file progress on stderr.
on_file(path) overwrites the current line with the label and truncated path.
finish() finalises the line with a newline.
"""
cols = shutil.get_terminal_size((80, 20)).columns
prefix = f" [scan] {label}... "
available = max(cols - len(prefix), 10)
def on_file(path):
rel = os.path.relpath(path)
if len(rel) > available:
rel = "..." + rel[-(available - 3):]
print(f"\r{prefix}{rel}\033[K", end="", file=sys.stderr, flush=True)
def finish():
print(f"\r{prefix}done\033[K", file=sys.stderr, flush=True)
return on_file, finish
def scan(target, depth=3, show_hidden=False): def scan(target, depth=3, show_hidden=False):
"""Run all analyses on the target directory and return a report dict.""" """Run all analyses on the target directory and return a report dict."""
report = {} report = {}
@ -24,16 +47,21 @@ def scan(target, depth=3, show_hidden=False):
report["tree"] = tree report["tree"] = tree
report["tree_rendered"] = render_tree(tree) report["tree_rendered"] = render_tree(tree)
print(" [scan] Classifying files...", file=sys.stderr) on_file, finish = _progress("Classifying files")
classified = classify_files(target, show_hidden=show_hidden) classified = classify_files(target, show_hidden=show_hidden, on_file=on_file)
finish()
report["file_categories"] = summarize_categories(classified) report["file_categories"] = summarize_categories(classified)
report["classified_files"] = classified report["classified_files"] = classified
print(" [scan] Detecting languages and counting lines...", file=sys.stderr) on_file, finish = _progress("Counting lines")
languages, loc = detect_languages(classified) languages, loc = detect_languages(classified, on_file=on_file)
finish()
report["languages"] = languages report["languages"] = languages
report["lines_of_code"] = loc report["lines_of_code"] = loc
report["large_files"] = find_large_files(classified)
on_file, finish = _progress("Checking for large files")
report["large_files"] = find_large_files(classified, on_file=on_file)
finish()
print(" [scan] Finding recently modified files...", file=sys.stderr) print(" [scan] Finding recently modified files...", file=sys.stderr)
report["recent_files"] = find_recent_files(target, show_hidden=show_hidden) report["recent_files"] = find_recent_files(target, show_hidden=show_hidden)

View file

@ -34,10 +34,11 @@ def _count_lines(filepath):
return 0 return 0
def detect_languages(classified_files): def detect_languages(classified_files, on_file=None):
"""Detect languages present and count lines of code per language. """Detect languages present and count lines of code per language.
Returns (languages_set, loc_by_language). Returns (languages_set, loc_by_language).
on_file(path) is called per source file, if provided.
""" """
source_files = [f for f in classified_files if f["category"] == "source"] source_files = [f for f in classified_files if f["category"] == "source"]
languages = set() languages = set()
@ -49,12 +50,17 @@ def detect_languages(classified_files):
languages.add(lang) languages.add(lang)
lines = _count_lines(f["path"]) lines = _count_lines(f["path"])
loc[lang] = loc.get(lang, 0) + lines loc[lang] = loc.get(lang, 0) + lines
if on_file:
on_file(f["path"])
return sorted(languages), loc return sorted(languages), loc
def find_large_files(classified_files): def find_large_files(classified_files, on_file=None):
"""Find files that are unusually large (>1000 lines or >10MB).""" """Find files that are unusually large (>1000 lines or >10MB).
on_file(path) is called per source file checked, if provided.
"""
source_files = [f for f in classified_files if f["category"] == "source"] source_files = [f for f in classified_files if f["category"] == "source"]
large = [] large = []
@ -68,5 +74,7 @@ def find_large_files(classified_files):
if reasons: if reasons:
large.append({"path": f["path"], "name": f["name"], large.append({"path": f["path"], "name": f["name"],
"reasons": reasons}) "reasons": reasons})
if on_file:
on_file(f["path"])
return large return large

View file

@ -86,10 +86,11 @@ def _classify_one(filepath):
return "unknown", desc return "unknown", desc
def classify_files(target, show_hidden=False): def classify_files(target, show_hidden=False, on_file=None):
"""Walk the target directory and classify every file. """Walk the target directory and classify every file.
Returns a list of dicts: {path, name, category, size, description}. Returns a list of dicts: {path, name, category, size, description}.
on_file(path) is called after each file is classified, if provided.
""" """
results = [] results = []
for root, dirs, files in os.walk(target): for root, dirs, files in os.walk(target):
@ -112,6 +113,8 @@ def classify_files(target, show_hidden=False):
"size": size, "size": size,
"description": desc, "description": desc,
}) })
if on_file:
on_file(full)
return results return results