Skip to content

Instantly share code, notes, and snippets.

@tos-kamiya
Last active September 21, 2025 09:01
Show Gist options
  • Save tos-kamiya/13c5f5f6919c9932159b9f8e7443f2b4 to your computer and use it in GitHub Desktop.
Save tos-kamiya/13c5f5f6919c9932159b9f8e7443f2b4 to your computer and use it in GitHub Desktop.
wc for unity project
#!/usr/bin/env python3
import os
import sys
def analyze_project_files_markdown(root_dir):
"""
Analyzes project files and outputs the statistics as Markdown tables.
"""
# Category definitions remain the same
UNITY_TEXT_EXTENSIONS = {
".cs",
".shader",
".cginc",
".asmdef",
".unity",
".prefab",
".asset",
".meta",
".json",
".xml",
".yml",
".yaml",
".txt",
".md",
".log",
".prefs",
".settings",
".graph",
".gitignore",
".gitattributes",
}
UNITY_TEXT_FILENAMES = {"manifest.json", "packages-lock.json"}
UNITY_BINARY_EXTENSIONS = {
".fbx",
".obj",
".blend",
".anim",
".controller",
".png",
".jpg",
".jpeg",
".tga",
".psd",
".exr",
".gif",
".bmp",
".tif",
".tiff",
".wav",
".mp3",
".ogg",
".aif",
".ttf",
".otf",
".mat",
".lighting",
".dll",
".so",
".bundle",
".a",
".db",
".bin",
}
EXCLUDE_DIRS = {".git", "Library", "Temp", "obj", "Logs", "UserSettings"}
# Data collection dictionaries
unity_text_stats = {}
unity_binary_stats = {}
other_stats = {}
# Data collection logic remains the same
for subdir, dirs, files in os.walk(root_dir):
dirs[:] = [d for d in dirs if d not in EXCLUDE_DIRS]
for filename in files:
filepath = os.path.join(subdir, filename)
_, ext = os.path.splitext(filename)
ext = ext.lower()
if ext in UNITY_TEXT_EXTENSIONS or filename in UNITY_TEXT_FILENAMES:
key = filename if filename in UNITY_TEXT_FILENAMES else ext
if key not in unity_text_stats:
unity_text_stats[key] = {"count": 0, "lines": 0}
unity_text_stats[key]["count"] += 1
try:
with open(filepath, "r", encoding="utf-8", errors="ignore") as f:
unity_text_stats[key]["lines"] += sum(1 for _ in f)
except (IOError, OSError):
pass
elif ext in UNITY_BINARY_EXTENSIONS:
if ext not in unity_binary_stats:
unity_binary_stats[ext] = {"count": 0}
unity_binary_stats[ext]["count"] += 1
else:
key = ext if ext else "(no extension)"
if key not in other_stats:
other_stats[key] = {"count": 0}
other_stats[key]["count"] += 1
# --- Markdown Print Function (with alignment) ---
def print_markdown_table(title, stats, show_lines=False):
print(f"### {title}\n")
# --- Define Headers ---
header_ext_str = "Extension"
header_count_str = "# files"
header_lines_str = "# lines"
# --- Pre-calculate column widths ---
ext_width = len(header_ext_str)
count_width = len(header_count_str)
lines_width = len(header_lines_str) if show_lines else 0
total_files = sum(data["count"] for data in stats.values())
total_lines = sum(data.get("lines", 0) for data in stats.values()) if show_lines else 0
# Create a list of all row data to calculate max widths
all_row_data = []
for key, data in stats.items():
all_row_data.append((key, str(data["count"]), str(data.get("lines", 0))))
# Add footer row for width calculation
all_row_data.append(
(
"**TOTAL**",
str(total_files),
str(total_lines) if show_lines else "0",
)
)
for row_data in all_row_data:
ext_width = max(ext_width, len(row_data[0]))
count_width = max(count_width, len(row_data[1]))
if show_lines:
lines_width = max(lines_width, len(row_data[2]))
# --- Print Header ---
header_ext = header_ext_str.ljust(ext_width)
header_count = header_count_str.rjust(count_width)
header = f"| {header_ext} | {header_count} "
# --- Print Separator ---
sep_ext = ":" + "-" * (ext_width - 1)
sep_count = "-" * (count_width - 1) + ":"
separator = f"| {sep_ext} | {sep_count} "
if show_lines:
header_lines = header_lines_str.rjust(lines_width)
header += f"| {header_lines} |"
sep_lines = "-" * (lines_width - 1) + ":"
separator += f"| {sep_lines} |"
else:
header += "|"
separator += "|"
print(header)
print(separator)
# --- Print Body ---
for key, data in sorted(stats.items()):
row = f"| {key.ljust(ext_width)} | {str(data['count']).rjust(count_width)} "
if show_lines:
lines = data.get("lines", 0)
row += f"| {str(lines).rjust(lines_width)} |"
else:
row += "|"
print(row)
# --- Print Footer ---
total_row = f"| {'**TOTAL**'.ljust(ext_width)} | {str(total_files).rjust(count_width)} "
if show_lines:
total_row += f"| {str(total_lines).rjust(lines_width)} |"
else:
total_row += "|"
print(total_row)
print() # Add a newline for spacing
print(f"## Project File Analysis for `{os.path.abspath(root_dir)}`\n")
print_markdown_table("Unity Text Files", unity_text_stats, show_lines=True)
print_markdown_table("Unity Binary Files", unity_binary_stats, show_lines=False)
print_markdown_table("Other Files", other_stats, show_lines=False)
if __name__ == "__main__":
start_path = "."
if len(sys.argv) > 1:
start_path = sys.argv[1]
if not os.path.isdir(start_path):
print(f"Error: Directory not found at '{start_path}'")
sys.exit(1)
analyze_project_files_markdown(start_path)
# # セッション要約: 2025-09-21 プロジェクト分析スクリプトの開発
#
# 本文書は2025年9月21日に行ったGeminiとの対話の要約です。この対話を通じて、Unityプロジェクトのファイル構成を分析するためのPythonスクリプトを作成しました。
#
# ## 対話の経緯
#
# 1. **最初の問い**: Unityプロジェクト内に存在する、テキストエディタで編集可能なファイルの拡張子について質問がありました。続いて、バイナリファイルの拡張子についても同様の質問がありました。
# 2. **スクリプト作成の要求**: 上記のファイル分類に基づき、プロジェクト内のファイル数や行数を集計するカスタム`wc`(Word Count)ツールとして、Pythonスクリプト (`unity_wc.py`) の作成が要求されました。
# 3. **第1イテレーション(基本分類)**: 最初に作成されたスクリプトは、テキストファイルの拡張子ホワイトリストに基づき、「テキストファイル」(行数カウントあり)と「バイナリファイル」(ファイル数のみ)の2つに分類するものでした。
# 4. **第2イテレーション(分類の改善)**: より詳細な分類が求められました。
# * バイナリファイルもホワイトリストで定義する。
# * カテゴリを「Unity関連テキスト」「Unity関連バイナリ」「その他」の3つに分ける。
# 5. **第3イテレーション(「その他」の扱いの改善)**: 「その他」カテゴリのファイルはテキストかバイナリか不明なため、行数カウントを行わず、ファイル数のみをカウントするよう修正が要求されました。
# 6. **第4イテレーション(Markdown出力)**: 最終的に、スクリプトの出力をドキュメントとして利用しやすいよう、Markdownのテーブル形式に整形することが要求されました。
# 7. **第5イテレーション(テキスト整形)**: Markdownテーブルの出力を、テキストファイルとして閲覧した際にも整形されて表示されるよう、スペースを追加して桁を揃えるよう要求がありました。
# 8. **第6イテレーション(再整形)**: テーブルのヘッダー名を短縮し、ヘッダー・区切り線・内容の各行でカラム幅が完全に一致するよう、桁揃えのロジックを再度修正しました。
# 9. **第7イテレーション(TOTAL行の書式変更)**: 最終行であるTOTALの数字を強調する`**`を削除し、よりシンプルな表示にするよう修正しました。
#
# ## 最終的な設計決定事項
#
# 一連の対話を通じて、以下の設計仕様を持つ `unity_wc.py` が完成しました。
#
# * **目的**: Unityプロジェクトのファイル構成を定量的に把握し、要約レポートを生成する。
# * **分類**: ファイルは以下の3つのカテゴリに分類する。
# 1. **Unity Text Files**: Unity開発で一般的に利用されるテキストファイル(`.cs`, `.shader`, `.asset`, `.meta`など)。拡張子のホワイトリストで定義。**ファイル数**と**総行数**を集計する。
# 2. **Unity Binary Files**: 3Dモデルやテクスチャなどの主要なバイナリファイル(`.fbx`, `.png`, `.mat`など)。拡張子のホワイトリストで定義。**ファイル数のみ**を集計する。
# 3. **Other Files**: 上記のいずれにも属さないファイル。**ファイル数のみ**を集計する。
# * **除外ディレクトリ**: パフォーマンスと分析の関連性を考慮し、Unityが自動生成する主要なディレクトリ(`Library`, `Temp`, `Logs`, `UserSettings`, `.git`など)は分析対象から除外する。
# * **出力形式**: 分析結果は**Markdown形式**で出力する。各カテゴリは見出しとテーブルで構成される。ヘッダー、区切り線、データ行の幅が完全に一致するようスペースでパディングされ、テキストファイルとして直接見た際の可読性も高められている。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment