Skip to content

Instantly share code, notes, and snippets.

@saropa
Created April 11, 2025 21:44
Show Gist options
  • Select an option

  • Save saropa/c63be2d1770b9721483e06029421323c to your computer and use it in GitHub Desktop.

Select an option

Save saropa/c63be2d1770b9721483e06029421323c to your computer and use it in GitHub Desktop.
#Requires -Version 7
<#
.SYNOPSIS
Counts lines, characters (bytes), and calculates total size (KB) of code/files in the parent directory.
Groups by file extension (incl. '[No Extension]') and top directories. Provides sorted/colored summaries.
.DESCRIPTION
Scans files, counts lines & gets size in bytes (using FileInfo.Length for accuracy across all types).
Groups results by extension (files without extensions are grouped as '[No Extension]') AND by directory level.
Outputs summaries using Format-Table with fixed-width columns and ANSI color:
1. Unfiltered Results by Extension (Sorted by Extension, includes Size KB).
2. Included Files Summary by Extension (> Min lines, Sorted by Lines Desc, Colored, includes Size KB).
3. Excluded File Details by Extension (Shows ALL excluded types found, Sorted by Lines Desc, Colored, includes Size KB). Clarifies that Min lines doesn't apply here.
4. Directory Summary (Top 1-2 levels > Min lines, Sorted by Lines Desc, Colored, includes Size KB). Based on Included files only.
5. Top 10 Included Files (by lines).
6. Top 10 Excluded Files (by raw lines).
Displays a colorful ASCII art logo.
.PARAMETER ExcludePatterns
Array of patterns to exclude files/directories. Supports wildcards (*).
.PARAMETER ExcludeExtensions
Array of file extensions (lowercase, leading dot) to exclude.
.PARAMETER MinReportLineCount
Minimum total lines for a group to appear in INCLUDED and DIRECTORY summary tables.
.PARAMETER ColorThresholdHigh, ColorThresholdMedium, ColorThresholdLow, ColorThresholdCharsHigh, ColorThresholdCharsMedium, ColorThresholdCharsLow, ColorThresholdKBHigh, ColorThresholdKBMedium, ColorThresholdKBLow
Thresholds for coloring Lines, Chars (Bytes), and Size (KB) columns in summary tables.
.NOTES
Version: 1.25 <-- Updated Version
Author: Saropa (Adapted for Line Counting by AI)
Copyright: © 2024 Saropa
Usage: Run from PowerShell. Use -Verbose for detailed logs. Ensure terminal supports ANSI colors.
#>
# --- Configuration ---
# $VerbosePreference = 'Continue' # <--- Enable for detailed debugging
param(
# --- ExcludePatterns ---
[string[]]$ExcludePatterns = @(
"*.g.dart", "*.freezed.dart", "lib/generated_plugin_registrant.dart",
"build/*", ".dart_tool/*", "bin/cache/*", "test/.test_coverage.dart",
"android/*", "ios/*", "dependency_overrides/*", "assets/*",
".idea/*", ".vscode/*"
),
# --- ExcludeExtensions ---
[string[]]$ExcludeExtensions = @(
'.png', '.jpg', '.jpeg', '.gif', '.bmp', '.svg', '.webp', '.ico', '.xcf',
'.zip', '.tar', '.gz', '.rar', '.7z', '.ttf', '.otf', '.woff', '.woff2',
'.lnk', '.mp3', '.wav', '.ogg', '.aac', '.mp4', '.mov', '.avi', '.webm',
'.pdf', '.exe', '.dll', '.so', '.dylib', '.bat', '.cmd', '.ps1', '.sh',
'.apk', '.aab', '.ipa', '.obj', '.o', '.a', '.lib', '.bin', '.dat', '.data',
'.dill', '.aar', '.jar', '.dex', '.class', '.pyc', '.pdb',
'.stamp', '.d', '.dep', '.cache', '.idx', '.vsidx', '.jks', '.keystore',
'.log', '.sqlite', '.db', '.lock', '.metadata', '.bak' # Restored .keystore
),
[int]$MinReportLineCount = 10,
# Color Thresholds - Lines
[long]$ColorThresholdHigh = 10000, [long]$ColorThresholdMedium = 1000, [long]$ColorThresholdLow = 100,
# Color Thresholds - Chars (Bytes)
[long]$ColorThresholdCharsHigh = 500000, [long]$ColorThresholdCharsMedium = 50000, [long]$ColorThresholdCharsLow = 5000,
# --- ADDED Color Thresholds - Size (KB) ---
[long]$ColorThresholdKBHigh = 5000, [long]$ColorThresholdKBMedium = 500, [long]$ColorThresholdKBLow = 50 # Default KB thresholds
)
#region Functions
# Function to display the Saropa "S" logo in ASCII art
function Show-SaropaLogo {
# ... (logo code omitted for brevity) ...
Write-Host "
`r`n`r`n`r`n
$([char]0x1b)[38;5;208m ....$([char]0x1b)[0m
$([char]0x1b)[38;5;208m `-+shdmNMMMMNmdhs+-$([char]0x1b)[0m
$([char]0x1b)[38;5;209m -odMMMNyo/-..````.++:+o+/-$([char]0x1b)[0m
$([char]0x1b)[38;5;215m `/dMMMMMM/` ``````````$([char]0x1b)[0m
$([char]0x1b)[38;5;220m `dMMMMMMMMNdhhhdddmmmNmmddhs+-$([char]0x1b)[0m
$([char]0x1b)[38;5;226m /MMMMMMMMMMMMMMMMMMMMMMMMMMMMMNh/$([char]0x1b)[0m
$([char]0x1b)[38;5;190m . :sdmNNNNMMMMMNNNMMMMMMMMMMMMMMMMm+$([char]0x1b)[0m
$([char]0x1b)[38;5;154m o `..~~~::~+==+~:/+sdNMMMMMMMMMMMo$([char]0x1b)[0m
$([char]0x1b)[38;5;118m m .+NMMMMMMMMMN$([char]0x1b)[0m
$([char]0x1b)[38;5;123m m+ :MMMMMMMMMm$([char]0x1b)[0m
$([char]0x1b)[38;5;87m /N: :MMMMMMMMM/$([char]0x1b)[0m
$([char]0x1b)[38;5;51m oNs. `+NMMMMMMMMo$([char]0x1b)[0m
$([char]0x1b)[38;5;45m :dNy/. ./smMMMMMMMMm:$([char]0x1b)[0m
$([char]0x1b)[38;5;39m `/dMNmhyso+++oosydNNMMMMMMMMMd/$([char]0x1b)[0m
$([char]0x1b)[38;5;33m .odMMMMMMMMMMMMMMMMMMMMdo-$([char]0x1b)[0m
$([char]0x1b)[38;5;57m `-+shdNNMMMMNNdhs+-$([char]0x1b)[0m
$([char]0x1b)[38;5;57m ````$([char]0x1b)[0m
" -ForegroundColor Green
$coloredCopyright = @"
$([char]0x1b)[38;5;195m© 2024 Saropa. All rights reserved.
$([char]0x1b)[38;5;117mhttps://saropa.com
"@
Write-Host $coloredCopyright
$email = "dev.tools@saropa.com"
$esc = [char]27
Write-Host " $esc]8;;mailto:$email$esc\Email $email$esc]8;;$esc\`n"
}
# Function to convert path to Unix-style separators
function Convert-PathToUnixStyle { param([string]$Path) ; if ([string]::IsNullOrEmpty($Path)) { return "" } ; return $Path -replace '\\', '/' }
# Function to normalize a directory path
function Normalize-DirectoryPath { param([string]$Path) ; if ([string]::IsNullOrEmpty($Path)) { return $null } ; try { $absPath = Resolve-Path -Path $Path -ErrorAction Stop ; $unixPath = Convert-PathToUnixStyle -Path $absPath.ProviderPath ; if (-not $unixPath.EndsWith('/')) { $unixPath += '/' } ; return $unixPath } catch { Write-Warning "Could not normalize directory path: $Path. Error: $($_.Exception.Message)" ; return $null } }
# Function to get the effective extension (lowercase), returns "[No Extension]" if none
function Get-EffectiveExtension {
param(
[System.IO.FileInfo]$FileInfo,
[string]$NoExtensionPlaceholder = '[No Extension]' # Define placeholder here
)
$fileName = $FileInfo.Name
$baseExtension = $FileInfo.Extension
$effectiveExtension = $baseExtension
# Handle multi-part extensions explicitly
if ($fileName.EndsWith('.g.dart', [System.StringComparison]::OrdinalIgnoreCase)) { $effectiveExtension = '.g.dart' }
elseif ($fileName.EndsWith('.freezed.dart', [System.StringComparison]::OrdinalIgnoreCase)) { $effectiveExtension = '.freezed.dart' }
elseif ($fileName.EndsWith('.config.yaml', [System.StringComparison]::OrdinalIgnoreCase)) { $effectiveExtension = '.config.yaml' }
# Handle dotfiles like '.gitignore' where Extension property might be empty or just '.'
elseif (-not [string]::IsNullOrEmpty($baseExtension) -and $baseExtension -eq $fileName -and $fileName.StartsWith('.')) { $effectiveExtension = $fileName }
# Final check and normalization
if (-not [string]::IsNullOrEmpty($effectiveExtension)) {
if (-not $effectiveExtension.StartsWith('.')) {
$effectiveExtension = '.' + $effectiveExtension
}
return $effectiveExtension.ToLowerInvariant()
} else {
# Return placeholder if no extension was found
return $NoExtensionPlaceholder
}
}
# Function to check if a file should be excluded
function Test-IsExcluded { param([Parameter(Mandatory=$true)][System.IO.FileInfo]$FileInfo, [Parameter(Mandatory=$true)][string[]]$Patterns, [Parameter(Mandatory=$true)][string]$NormalizedBaseDirectory, [Parameter(Mandatory=$true)][string[]]$ExcludedExtensions) ; Write-Verbose "--- Testing Exclusions for: $($FileInfo.FullName) ---" ; $fileExtension = Get-EffectiveExtension -FileInfo $FileInfo -NoExtensionPlaceholder $null ; if (-not [string]::IsNullOrEmpty($fileExtension)) { if ($ExcludedExtensions -contains $fileExtension) { Write-Verbose "[RESULT] Excluded: Extension type '$fileExtension' is in the extension exclusion list." ; return $true } else { Write-Verbose "[Check 1 OK] Extension '$fileExtension' not in extension exclusion list." } } else { Write-Verbose "[Check 1 SKIPPED] File has no detectable extension." } ; $unixFullPath = Convert-PathToUnixStyle -Path $FileInfo.FullName ; $relativePathString = $null ; if ($NormalizedBaseDirectory -ne $null -and $unixFullPath.StartsWith($NormalizedBaseDirectory, [System.StringComparison]::OrdinalIgnoreCase)) { $relativePathString = $unixFullPath.Substring($NormalizedBaseDirectory.Length) ; Write-Verbose "[Check 2 Path] Relative Path: '$relativePathString'" } else { Write-Verbose "[Check 2 Path] Cannot determine relative path or file '$unixFullPath' outside base '$NormalizedBaseDirectory'." ; Write-Verbose "[RESULT] Included: Cannot apply path patterns." ; return $false } ; foreach ($pattern in $Patterns) { $patternCheckResult = $false ; $matchType = "Unknown" ; $normalizedPattern = $pattern ; if ($normalizedPattern.StartsWith("*.")) { $matchType = "Suffix" ; if ($FileInfo.Name -like $normalizedPattern) { $patternCheckResult = $true } } elseif ($normalizedPattern.EndsWith("/*")) { $matchType = "Directory" ; $dirPattern = $normalizedPattern.Substring(0, $normalizedPattern.Length - 1) ; if ($relativePathString -like "$dirPattern*") { $patternCheckResult = $true } } else { $matchType = "Exact" ; if ($relativePathString -eq $normalizedPattern) { $patternCheckResult = $true } } ; if ($patternCheckResult) { Write-Verbose "[RESULT] Excluded: Relative path '$relativePathString' matches ($matchType) pattern '$pattern'." ; return $true } else { Write-Verbose "[Check 2 Pattern OK] Relative path '$relativePathString' does NOT match ($matchType) pattern '$pattern'." } } ; Write-Verbose "[RESULT] Included: No exclusion rules matched." ; return $false }
# Function to get top directory levels (1 or 2), handles root/unknown
function Get-DirectoryLevels {
param(
[string]$RelativePath,
[string]$RootPlaceholder = '[Root Directory]',
[string]$RootFilePlaceholder = '[Project Root File]'
)
if ([string]::IsNullOrEmpty($RelativePath)) { return $RootPlaceholder } # Or maybe '[Unknown Path]'?
$parts = $RelativePath.Split('/')
if ($parts.Count -le 1) {
# It's a file directly in the base directory
return $RootFilePlaceholder
} elseif ($parts.Count -eq 2) {
# e.g., "lib/file.txt" -> return "lib"
return $parts[0]
} else {
# e.g., "lib/src/file.txt" -> return "lib/src"
return "$($parts[0])/$($parts[1])"
}
}
# Function to format number with ANSI colors based on thresholds (4 Colors)
function Format-NumberWithColor {
param(
[long]$Number,
[long]$ThresholdHigh,
[long]$ThresholdMedium,
[long]$ThresholdLow
)
$esc = [char]0x1b
$reset = "$esc[0m"
$red = "$esc[38;5;196m" # Bright Red
$yellow = "$esc[38;5;220m" # Bright Yellow
$blue = "$esc[38;5;39m" # Bright Blue
$formattedNumber = "{0:N0}" -f $Number
if ($Number -ge $ThresholdHigh) { return "$red$formattedNumber$reset" }
elseif ($Number -ge $ThresholdMedium) { return "$yellow$formattedNumber$reset" }
elseif ($Number -ge $ThresholdLow) { return "$blue$formattedNumber$reset" }
else { return $formattedNumber }
}
#endregion Functions
# --- Main Script ---
Show-SaropaLogo
$parentDirRaw = $null; $normalizedParentDir = $null
try {
$parentDirRaw = Split-Path -Path $PSScriptRoot -Parent -ErrorAction Stop
$normalizedParentDir = Normalize-DirectoryPath -Path $parentDirRaw
if ($normalizedParentDir -eq $null) { throw "Failed." }
}
catch { Write-Error "Could not determine/normalize parent directory ($PSScriptRoot). Error: $($_.Exception.Message)"; Exit 1 }
$normalizedExcludeExtensions = $ExcludeExtensions | ForEach-Object { $_.ToLowerInvariant() }
Write-Host "-----------------------------------------------------"
Write-Host " Counting Lines & Chars (Bytes) of Code (v1.25)" # <-- Updated Version
Write-Host " Target Directory: $normalizedParentDir"
Write-Host " Minimum Lines for Included/Directory Tables: $MinReportLineCount"
Write-Host " Color Thresholds (Lines): High >= $ColorThresholdHigh (Red), Medium >= $ColorThresholdMedium (Yellow), Low >= $ColorThresholdLow (Blue)"
Write-Host " Color Thresholds (Chars): High >= $ColorThresholdCharsHigh (Red), Medium >= $ColorThresholdCharsMedium (Yellow), Low >= $ColorThresholdCharsLow (Blue)"
Write-Host " Color Thresholds (SizeKB): High >= $ColorThresholdKBHigh (Red), Medium >= $ColorThresholdKBMedium (Yellow), Low >= $ColorThresholdKBLow (Blue)" # ADDED KB thresholds info
Write-Host ""
Write-Host "Exclusion Patterns Applied:" ; $ExcludePatterns | ForEach-Object { Write-Host " - $_" }
Write-Host "" ; Write-Host "Excluded Extension Types:"
$maxWidth = [Math]::Max(60, $Host.UI.RawUI.WindowSize.Width - 5) ; $currentLine = " - " ; $firstExt = $true
foreach ($ext in $normalizedExcludeExtensions) { $extWithComma = if ($firstExt) { $ext } else { ", $ext" } ; if (($currentLine.Length + $extWithComma.Length) -gt $maxWidth) { Write-Host $currentLine; $currentLine = " $ext" } else { $currentLine += $extWithComma } ; $firstExt = $false } ; Write-Host $currentLine
Write-Host "-----------------------------------------------------`n"
$totalFilesFound = 0 ; [long]$totalLinesFound = 0 ; [long]$totalCharsFound = 0
$processedFilesData = [System.Collections.Generic.List[PSCustomObject]]::new()
$noExtensionPlaceholder = '[No Extension]' # Consistent placeholder
$rootFilePlaceholder = '[Project Root File]'
$rootPlaceholder = '[Root Directory]'
$unknownPathPlaceholder = '[Unknown Path]' # For files outside base dir
# --- Scan, Count, and Process Files ---
Write-Host "Scanning files recursively..."
$files = Get-ChildItem -Path $normalizedParentDir -Recurse -File -ErrorAction SilentlyContinue
$totalFileCount = $files.Count
Write-Host "Found $totalFileCount files to process..."
Write-Host "Processing files, counting lines & characters (bytes), and determining exclusions..."
$progressCount = 0 ; $updateInterval = [Math]::Max(1, [Math]::Floor($totalFileCount / 50))
foreach ($file in $files) {
$progressCount++ ; if ($totalFileCount -gt 0) { if ($progressCount % $updateInterval -eq 0 -or $progressCount -eq $totalFileCount) { $percentComplete = [Math]::Floor(($progressCount / $totalFileCount) * 100) ; Write-Progress -Activity "Processing Files" -Status "File $progressCount of $totalFileCount ($percentComplete%)" -PercentComplete $percentComplete -CurrentOperation $file.Name } }
[long]$lineCount = 0 ; [long]$rawLineCount = 0
[long]$charCount = 0 ; [long]$rawCharCount = 0
# --- FIX 3: Get Character/Byte Count using FileInfo.Length for accuracy ---
$rawCharCount = $file.Length
# Get Line Count (only if needed or for raw count)
try {
$reader = [System.IO.StreamReader]::new($file.FullName)
while ($reader.ReadLine() -ne $null) { $rawLineCount++ }
$reader.Close() ; $reader.Dispose()
} catch { Write-Verbose "[WARNING] Could not read file for line count: $($file.FullName) - Error: $($_.Exception.Message)" }
# Determine effective extension (gets placeholder if none)
$effectiveExtension = Get-EffectiveExtension -FileInfo $file -NoExtensionPlaceholder $noExtensionPlaceholder
# Check exclusions
$isExcluded = Test-IsExcluded -FileInfo $file -Patterns $ExcludePatterns -NormalizedBaseDirectory $normalizedParentDir -ExcludedExtensions $normalizedExcludeExtensions
# Set included counts (0 if excluded)
if (-not $isExcluded) { $lineCount = $rawLineCount ; $charCount = $rawCharCount }
else { $lineCount = 0 ; $charCount = 0 } # Effective counts are 0 for excluded
# Determine Directory Level
$unixFullPath = Convert-PathToUnixStyle -Path $file.FullName
$relativePathString = $null
$dirLevel = $unknownPathPlaceholder # Default if path normalization fails
if ($normalizedParentDir -ne $null -and $unixFullPath.StartsWith($normalizedParentDir, [System.StringComparison]::OrdinalIgnoreCase)) {
$relativePathString = $unixFullPath.Substring($normalizedParentDir.Length)
$dirLevel = Get-DirectoryLevels -RelativePath $relativePathString -RootFilePlaceholder $rootFilePlaceholder -RootPlaceholder $rootPlaceholder
}
$processedFilesData.Add([PSCustomObject]@{
FullName = $file.FullName
Extension = $effectiveExtension # Now includes placeholder
RawLines = $rawLineCount
RawChars = $rawCharCount # Accurate byte count
Lines = $lineCount
Chars = $charCount # Accurate byte count if included
IsExcluded = $isExcluded
DirectoryLevel = $dirLevel # Now includes placeholders
})
$totalFilesFound++
$totalLinesFound += $rawLineCount
$totalCharsFound += $rawCharCount # Sum of all file lengths (bytes)
}
if ($totalFileCount -gt 0) { Write-Progress -Activity "Processing Files" -Completed }
# --- Process Results ---
Write-Host "`nCalculating summaries..."
$filteredFilesData = $processedFilesData | Where-Object { -not $_.IsExcluded }
$excludedFilesData = $processedFilesData | Where-Object { $_.IsExcluded }
# --- MODIFIED Get-SummaryData to handle placeholders ---
function Get-SummaryData {
param(
$Data,
[string]$GroupByProperty, # 'Extension' or 'DirectoryLevel'
[string]$MinCountProperty = 'Lines',
[int]$MinCountValue = 0,
[bool]$UseRawCounts = $false
)
# Placeholders are now part of the data, no separate 'WithoutValue' needed
$result = @{ Summary = @() }
if ($null -eq $Data -or $Data.Count -eq 0) { return $result }
$linesProp = if ($UseRawCounts) { 'RawLines' } else { 'Lines' }
$charsProp = if ($UseRawCounts) { 'RawChars' } else { 'Chars' }
try {
# Group by the property (which includes placeholders like '[No Extension]')
$groupedData = $Data | Group-Object $GroupByProperty -ErrorAction Stop
# Aggregate data
$summary = $groupedData | Select-Object @{Name=$GroupByProperty; Expression={$_.Name}}, # Name is now the extension or dir level (or placeholder)
@{Name='Files'; Expression={$_.Count}},
@{Name='Lines'; Expression={ ($_.Group.$linesProp | Measure-Object -Sum -ErrorAction SilentlyContinue).Sum -as [long] }},
@{Name='Chars'; Expression={ ($_.Group.$charsProp | Measure-Object -Sum -ErrorAction SilentlyContinue).Sum -as [long] }}, # Chars now accurately reflects bytes
@{Name='SizeKB'; Expression={ [Math]::Round( (($_.Group.$charsProp | Measure-Object -Sum -ErrorAction SilentlyContinue).Sum -as [long]) / 1024 ) }}
# Apply minimum filter based on the specified property and value
$summary = $summary | Where-Object { $_.$MinCountProperty -ge $MinCountValue } -ErrorAction Stop
$result.Summary = $summary
Write-Verbose "Get-SummaryData: Grouped by '$GroupByProperty'. Found $($summary.Count) groups where '$MinCountProperty' >= $MinCountValue (using $(if($UseRawCounts){'Raw'}else{'Effective'}) counts)."
} catch {
Write-Warning "Get-SummaryData: Error during processing for '$GroupByProperty'. Error: $($_.Exception.Message)"
# Reset on error
$result.Summary = @()
}
return $result
}
# --- Calculate Summaries ---
# 1. Unfiltered (Raw counts, Min Files = 0 - show all found extensions/placeholders)
$unfilteredResult = Get-SummaryData -Data $processedFilesData -GroupByProperty 'Extension' -MinCountProperty 'Files' -MinCountValue 0 -UseRawCounts $true
$unfilteredSummaryTable = $unfilteredResult.Summary | Sort-Object Extension
# 2. Included (Effective counts, Min Lines = $MinReportLineCount)
$includedExtResult = Get-SummaryData -Data $filteredFilesData -GroupByProperty 'Extension' -MinCountProperty 'Lines' -MinCountValue $MinReportLineCount
$includedSummaryTableExt = $includedExtResult.Summary | Sort-Object Lines -Descending
# 3. Excluded (Raw counts, Min Files = 1 - show all *found* excluded types/placeholders)
$excludedExtResult = Get-SummaryData -Data $excludedFilesData -GroupByProperty 'Extension' -MinCountProperty 'Files' -MinCountValue 1 -UseRawCounts $true
$excludedSummaryTableExt = $excludedExtResult.Summary | Sort-Object Lines -Descending # Sort by Raw Lines desc
# 4. Directory (Effective counts from Included Files, Min Lines = $MinReportLineCount)
$directoryResult = Get-SummaryData -Data $filteredFilesData -GroupByProperty 'DirectoryLevel' -MinCountProperty 'Lines' -MinCountValue $MinReportLineCount
$directorySummaryTable = $directoryResult.Summary | Sort-Object Lines -Descending
# 5. Top Included Files
$topIncludedFiles = $filteredFilesData | Sort-Object Lines -Descending | Select-Object -First 10 FullName, Lines, Chars
# 6. Top Excluded Files
$topExcludedFiles = $excludedFilesData | Sort-Object RawLines -Descending | Select-Object -First 10 FullName, @{Name="Lines (Raw)"; Expression={$_.RawLines}}, @{Name="Chars (Raw)"; Expression={$_.RawChars}}
# Calculate Totals
$totalFilesFiltered = ($filteredFilesData | Measure-Object).Count
[long]$totalLinesFiltered = ($filteredFilesData.Lines | Measure-Object -Sum -ErrorAction SilentlyContinue).Sum
[long]$totalCharsFiltered = ($filteredFilesData.Chars | Measure-Object -Sum -ErrorAction SilentlyContinue).Sum # Uses accurate Chars
[long]$totalSizeKBFiltered = [Math]::Round($totalCharsFiltered / 1024)
$totalFilesExcluded = ($excludedFilesData | Measure-Object).Count
[long]$totalLinesExcluded = ($excludedFilesData.RawLines | Measure-Object -Sum -ErrorAction SilentlyContinue).Sum
[long]$totalCharsExcluded = ($excludedFilesData.RawChars | Measure-Object -Sum -ErrorAction SilentlyContinue).Sum # Uses accurate RawChars
[long]$totalSizeKBExcluded = [Math]::Round($totalCharsExcluded / 1024)
[long]$totalSizeKBFound = [Math]::Round($totalCharsFound / 1024)
# --- Display Results ---
# --- MODIFIED: Added SizeKB COLORING to Format Definitions ---
$commonFormatProperties = @(
@{Name='Files'; Expression={"{0:N0}" -f $_.Files}; Alignment='Right'; Width=16},
@{Name='Lines'; Expression={ Format-NumberWithColor -Number $_.Lines -ThresholdHigh $ColorThresholdHigh -ThresholdMedium $ColorThresholdMedium -ThresholdLow $ColorThresholdLow }; Alignment='Right'; Width=16},
@{Name='Chars'; Expression={ Format-NumberWithColor -Number $_.Chars -ThresholdHigh $ColorThresholdCharsHigh -ThresholdMedium $ColorThresholdCharsMedium -ThresholdLow $ColorThresholdCharsLow }; Alignment='Right'; Width=18},
# --- ADDED KB Coloring ---
@{Name='Size (KB)'; Expression={ Format-NumberWithColor -Number $_.SizeKB -ThresholdHigh $ColorThresholdKBHigh -ThresholdMedium $ColorThresholdKBMedium -ThresholdLow $ColorThresholdKBLow }; Alignment='Right'; Width=16}
)
$unfilteredFormat = @( @{Name='Extension'; Expression={$_.Extension}; Width=30} ) + $commonFormatProperties
$includedFormat = @( @{Name='Extension'; Expression={$_.Extension}; Width=30} ) + $commonFormatProperties
$excludedFormat = @( @{Name='Extension'; Expression={$_.Extension}; Width=30} ) + $commonFormatProperties # Using colored props now
$directoryFormat = @( @{Name='Directory'; Expression={$_.DirectoryLevel}; Width=50} ) + $commonFormatProperties
# Top File formats remain unchanged
$topIncludedFormat = @(
@{Name='FullName'; Expression={$_.FullName}; Width=120},
@{Name='Lines'; Expression={"{0:N0}" -f $_.Lines}; Alignment='Right'; Width=16},
@{Name='Chars'; Expression={"{0:N0}" -f $_.Chars}; Alignment='Right'; Width=18} # Chars is accurate byte count now
)
$topExcludedFormat = @(
@{Name='FullName'; Expression={$_.FullName}; Width=120},
@{Name='Lines (Raw)'; Expression={"{0:N0}" -f $_.'Lines (Raw)'}; Alignment='Right'; Width=16},
@{Name='Chars (Raw)'; Expression={"{0:N0}" -f $_.'Chars (Raw)'}; Alignment='Right'; Width=18} # Chars is accurate byte count now
)
# 1. Unfiltered Results by Extension
Write-Host "`n--- UNFILTERED RESULTS (All Files By Extension) ---"
Write-Host "Shows all file types (incl. $noExtensionPlaceholder) found (using raw counts). Sorted by Extension."
if ($unfilteredSummaryTable.Count -gt 0) {
$unfilteredSummaryTable | Format-Table -Property $unfilteredFormat
} else { Write-Host "No files found." }
# --- REMOVED "WithoutValue" reporting - it's now in the table ---
Write-Host ("`nTotal Files (Unfiltered Scan): {0:N0}" -f $totalFilesFound)
Write-Host ("Total Lines (Unfiltered Scan): {0:N0}" -f $totalLinesFound)
Write-Host ("Total Chars (Bytes) (Unfiltered Scan): {0:N0}" -f $totalCharsFound) # Clarified Chars = Bytes
Write-Host ("Total Size KB (Unfiltered Scan): {0:N0}" -f $totalSizeKBFound)
# 2. Included Files Summary by Extension
Write-Host "`n--- INCLUDED FILES SUMMARY (Included Files > $MinReportLineCount Lines per Group) ---"
Write-Host "Sorted by Line Count Descending (Colored). Types below threshold omitted. Includes '$noExtensionPlaceholder'."
Write-Host ""
if ($includedSummaryTableExt.Count -gt 0) {
$includedSummaryTableExt | Format-Table -Property $includedFormat
} else { Write-Host "No included groups met the minimum line count threshold of $MinReportLineCount." }
# --- REMOVED "WithoutValue" reporting ---
Write-Host ("`nTotal Included Files (Actual): {0:N0}" -f $totalFilesFiltered)
Write-Host ("Total Included Lines (Actual): {0:N0}" -f $totalLinesFiltered)
Write-Host ("Total Included Chars (Bytes) (Actual): {0:N0}" -f $totalCharsFiltered) # Clarified Chars = Bytes
Write-Host ("Total Included Size KB (Actual): {0:N0}" -f $totalSizeKBFiltered)
# 3. Excluded File Details by Extension
Write-Host "`n--- EXCLUDED FILE DETAILS (Shows All Excluded Types Found) ---"
Write-Host "Sorted by Line Count Descending (Colored). Shows RAW counts. Includes '$noExtensionPlaceholder'."
Write-Host "NOTE: This table lists ALL excluded types/groups found (min 1 file), ignoring the '$MinReportLineCount' parameter."
Write-Host ""
if ($excludedSummaryTableExt.Count -gt 0) {
$excludedSummaryTableExt | Format-Table -Property $excludedFormat
} else { Write-Host "No excluded files were found." }
# --- REMOVED "WithoutValue" reporting and listing ---
Write-Host ("`nTotal Excluded Files (Actual): {0:N0}" -f $totalFilesExcluded)
Write-Host ("Total Excluded Lines (Raw): {0:N0}" -f $totalLinesExcluded)
Write-Host ("Total Excluded Chars (Bytes) (Raw): {0:N0}" -f $totalCharsExcluded) # Clarified Chars = Bytes
Write-Host ("Total Excluded Size KB (Raw): {0:N0}" -f $totalSizeKBExcluded)
# 4. Directory Summary
Write-Host "`n--- DIRECTORY SUMMARY (Top 1-2 Levels > $MinReportLineCount Lines per Directory Group) ---"
Write-Host "Counts lines/chars/size from INCLUDED files only. Sorted by Line Count Descending (Colored). Includes '$rootFilePlaceholder' etc."
Write-Host ""
if ($directorySummaryTable.Count -gt 0) {
$directorySummaryTable | Format-Table -Property $directoryFormat
} else { Write-Host "No directories met the minimum line count threshold of $MinReportLineCount based on included files." }
# --- REMOVED "WithoutValue" reporting ---
# 5. Top 10 Included Files
Write-Host "`n--- TOP 10 INCLUDED FILES (By Line Count) ---"
if ($topIncludedFiles.Count -gt 0) {
$topIncludedFiles | Format-Table -Property $topIncludedFormat
} else { Write-Host "No included files found." }
# 6. Top 10 Excluded Files
Write-Host "`n--- TOP 10 EXCLUDED FILES (By Raw Line Count) ---"
if ($topExcludedFiles.Count -gt 0) {
$topExcludedFiles | Format-Table -Property $topExcludedFormat
} else { Write-Host "No excluded files found."}
Write-Host "`n-----------------------------------------------------"
Write-Host " Line Count Complete."
Write-Host "-----------------------------------------------------`n"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment