treeScannerASCII/scanner.py

211 lines
7.9 KiB
Python
Raw Normal View History

2025-04-26 03:02:58 +02:00
#!/usr/bin/env python3
"""
2025-04-26 04:13:07 +02:00
TreeScanner: Verzeichnisbaum-Ausgabe mit ASCII-Art.
Standalone-Ausführung unterstützt folgende CLI-Parameter:
root_path Pfad des Stammverzeichnisses (Default: aktuelles Verzeichnis).
-n, --max-files-per-dir Maximale Anzahl Dateien pro Verzeichnis (Default: 2).
-d, --max-depth Maximale Rekursionstiefe (Default: unbegrenzt).
--no-align-comments Deaktiviert das Ausrichten der Kommentare.
-h, --help Diese Hilfe anzeigen und Programm beenden.
2025-04-26 03:02:58 +02:00
"""
import os
2025-04-26 04:13:07 +02:00
import argparse
2025-04-26 03:02:58 +02:00
from typing import Optional, List
class TreeScannerConfig:
2025-04-26 04:13:07 +02:00
"""
Konfigurationsklasse für den TreeScanner.
Attributes:
root_path (str): Pfad des Stammverzeichnisses.
folder_icon (str): Icon für Ordner.
file_icon (str): Icon für Dateien und Platzhalter.
max_files_per_dir (int): Maximale Anzahl Dateien, die pro Verzeichnis angezeigt werden.
max_depth (Optional[int]): Maximale Tiefe der Rekursion (None = unbeschränkt).
align_comments (bool): Ob Kommentare am Zeilenende ausgerichtet werden sollen.
"""
def __init__(
self,
root_path: str = ".",
folder_icon: str = "\U0001F4C1",
file_icon: str = "\U0001F4C4",
max_files_per_dir: int = 2,
max_depth: Optional[int] = None,
align_comments: bool = True,
):
"""
Args:
root_path (str): Pfad des Stammverzeichnisses.
folder_icon (str): Icon für Ordner.
file_icon (str): Icon für Dateien und Platzhalter.
max_files_per_dir (int): Maximale Anzahl Dateien pro Verzeichnis.
max_depth (Optional[int]): Maximale Rekursionstiefe, None = unlimitiert.
align_comments (bool): Kommentare ausrichten, wenn True.
"""
2025-04-26 03:02:58 +02:00
self.root_path = root_path
self.folder_icon = folder_icon
self.file_icon = file_icon
self.max_files_per_dir = max_files_per_dir
self.max_depth = max_depth
self.align_comments = align_comments
class TreeScanner:
2025-04-26 04:13:07 +02:00
"""
Klasse zum Scannen von Verzeichnissen und Erzeugen einer ASCII-Baumstruktur.
"""
2025-04-26 03:02:58 +02:00
def __init__(self, config: TreeScannerConfig):
2025-04-26 04:13:07 +02:00
"""
Args:
config (TreeScannerConfig): Konfiguration für den Scanner.
"""
2025-04-26 03:02:58 +02:00
self.config = config
def scan_directory(self, path: str, depth: int = 0, prefix: str = "") -> List[str]:
2025-04-26 04:13:07 +02:00
"""
Scannt ein Verzeichnis und gibt eine Liste von ASCII-Zeilen zurück.
Args:
path (str): Pfad des zu scannenden Verzeichnisses.
depth (int): Aktuelle Rekursionstiefe.
prefix (str): Präfix für Einrückung und Connectoren.
Returns:
List[str]: Zeilen mit ASCII-Baumstruktur für dieses Verzeichnis.
"""
lines: List[str] = []
2025-04-26 03:02:58 +02:00
try:
entries = sorted(os.listdir(path))
except PermissionError:
2025-04-26 04:13:07 +02:00
# Zugriff verweigert: Spezielle Rückgabe, kein Rekursionsaufruf
2025-04-26 03:02:58 +02:00
return [f"{prefix}└── [Zugriff verweigert] {path}"]
2025-04-26 04:13:07 +02:00
# Aufteilen in Ordner und Dateien
2025-04-26 03:02:58 +02:00
folders = [e for e in entries if os.path.isdir(os.path.join(path, e))]
files = [e for e in entries if os.path.isfile(os.path.join(path, e))]
2025-04-26 04:13:07 +02:00
# 1) Verzeichnisse verarbeiten
2025-04-26 03:02:58 +02:00
for idx, folder in enumerate(folders):
folder_path = os.path.join(path, folder)
2025-04-26 04:13:07 +02:00
# ├──, wenn nicht letzter Ordner oder noch Dateien folgen, sonst └──
2025-04-26 03:02:58 +02:00
connector = "├── " if idx < len(folders) - 1 or files else "└── "
2025-04-26 04:13:07 +02:00
lines.append(f"{prefix}{connector}{self.config.folder_icon} {folder}")
# Rekursive Ausgabe, falls max_depth nicht überschritten
2025-04-26 03:02:58 +02:00
if self.config.max_depth is None or depth < self.config.max_depth:
2025-04-26 04:13:07 +02:00
# Verlängerung des Präfixes: '│ ' oder Leerraum
2025-04-26 03:02:58 +02:00
extension = "" if idx < len(folders) - 1 or files else " "
lines.extend(self.scan_directory(folder_path, depth + 1, prefix + extension))
2025-04-26 04:13:07 +02:00
# 2) Dateien plus Platzhalter als kombinierte Liste behandeln
visible_files = files[: self.config.max_files_per_dir]
remaining = len(files) - len(visible_files)
# Kombinieren: echte Dateien + Platzhalter
combined = visible_files.copy()
if remaining > 0:
combined.append(f"<und {remaining} weitere Dateien>")
2025-04-26 03:02:58 +02:00
2025-04-26 04:13:07 +02:00
# Ausgabe mit Datei-Icon für echte Dateien UND Platzhalter
for idx, name in enumerate(combined):
# ├── für alle außer dem letzten Eintrag, sonst └──
connector = "├── " if idx < len(combined) - 1 else "└── "
# Icon hinzufügen (auch beim Platzhalter)
lines.append(f"{prefix}{connector}{self.config.file_icon} {name}")
2025-04-26 03:02:58 +02:00
return lines
def align_lines_with_comments(self, lines: List[str]) -> List[str]:
2025-04-26 04:13:07 +02:00
"""
Richtet alle Zeilen so aus, dass Kommentare am gleichen Spaltenindex stehen.
Args:
lines (List[str]): Liste der Baumstruktur-Zeilen ohne Kommentare.
Returns:
List[str]: Zeilen mit ausgerichteten Kommentar-Platzhaltern (#).
"""
# Bestimme Länge der längsten Zeile (ohne Trailing Spaces)
2025-04-26 03:02:58 +02:00
max_length = max(len(line.rstrip()) for line in lines)
2025-04-26 04:13:07 +02:00
aligned: List[str] = []
for line in lines:
text = line.rstrip()
# Padding-Breite: definierte Spaltenausrichtung (+2 Leerzeichen)
padding = " " * (max_length - len(text) + 2)
aligned.append(text + padding + "# ")
return aligned
2025-04-26 03:02:58 +02:00
def generate_tree(self) -> str:
2025-04-26 04:13:07 +02:00
"""
Generiert den vollständigen Verzeichnisbaum als String.
Returns:
str: Mehrzeiliger String mit Ordner-Icon, Ordner- und Dateienstruktur.
"""
# Stammverzeichnisname ermitteln
2025-04-26 03:02:58 +02:00
root_name = os.path.basename(os.path.abspath(self.config.root_path)) or self.config.root_path
lines = [f"{self.config.folder_icon} {root_name}/"]
2025-04-26 04:13:07 +02:00
# Baumstruktur dahinter generieren
2025-04-26 03:02:58 +02:00
lines += self.scan_directory(self.config.root_path)
2025-04-26 04:13:07 +02:00
# Optional Kommentare ausrichten
2025-04-26 03:02:58 +02:00
if self.config.align_comments:
lines = self.align_lines_with_comments(lines)
return "\n".join(lines)
def main():
2025-04-26 04:13:07 +02:00
"""
Standalone-Ausführung mit CLI-Parameter-Unterstützung.
Parse CLI-Argumente und generiere tree.txt.
"""
# CLI-Parser konfigurieren
parser = argparse.ArgumentParser(
description="Generiert eine ASCII-Baumstruktur eines Verzeichnisses."
)
# Positionsargument: root_path (optional)
parser.add_argument(
"root_path",
nargs="?",
default=".",
help="Pfad des Stammverzeichnisses (default: aktuelles Verzeichnis)."
)
# Optionale Flags und Parameter
parser.add_argument(
"-n", "--max-files-per-dir",
type=int,
default=2,
help="Maximale Anzahl Dateien pro Verzeichnis (default: 2)."
)
parser.add_argument(
"-d", "--max-depth",
type=int,
help="Maximale Rekursionstiefe; unbegrenzt, wenn nicht gesetzt."
)
parser.add_argument(
"--no-align-comments",
action="store_false",
dest="align_comments",
help="Deaktiviert das Ausrichten der Kommentare am Zeilenende."
)
# CLI-Argumente einlesen
args = parser.parse_args()
# Konfiguration auf Basis der CLI-Argumente erstellen
config = TreeScannerConfig(
root_path=args.root_path,
max_files_per_dir=args.max_files_per_dir,
max_depth=args.max_depth,
align_comments=args.align_comments
)
2025-04-26 03:02:58 +02:00
scanner = TreeScanner(config)
tree_output = scanner.generate_tree()
2025-04-26 04:13:07 +02:00
# Ausgabe in tree.txt schreiben
2025-04-26 03:02:58 +02:00
with open("tree.txt", "w", encoding="utf-8") as f:
f.write(tree_output + "\n")
if __name__ == "__main__":
main()