DirwTools.psm1

function Format-FileSize {
    param ([long]$bytes)
    if ($bytes -ge 1GB) { "{0:N1} GB" -f ($bytes / 1GB) }
    elseif ($bytes -ge 1MB) { "{0:N1} MB" -f ($bytes / 1MB) }
    elseif ($bytes -ge 1KB) { "{0:N1} KB" -f ($bytes / 1KB) }
    else { "$bytes B" }
}

function Get-FolderSize {
    param ($folderPath)
    try {
        (Get-ChildItem -LiteralPath $folderPath -Recurse -Force -File -ErrorAction SilentlyContinue |
         Measure-Object -Property Length -Sum).Sum
    } catch {
        0
    }
}

function dirw {
<#
.SYNOPSIS
  Multi-column advanced DIR-like file listing for PowerShell.
.DESCRIPTION
  Shows files and folders in multi-column format, like DIR /W, with color and (otional) file info.
.PARAMETER Target
  Folder to list (default: current directory)
.PARAMETER l
  Show sizes/dates (long format)
.PARAMETER s
  Calculate folder sizes (recursive, slow on big dirs)
.PARAMETER a
  Show hidden files/directorys
.PARAMETER f
  Filter pattern (eg. *.cs)
.EXAMPLE
  dirw -l -a
.NOTES
  Author: Michael.Voitovich@gmail.com
#>

    [CmdletBinding(PositionalBinding = $false)]
    param (
        [Parameter(Position=0)]
        [string]$Target = ".",

        [string]$f,             # short for -Filter
        [switch]$l,             # short for -Long
        [switch]$s,             # short for -CalculateFolderSize
        [switch]$a,             # short for -ShowHidden
        [switch]$h,             # short for -Help

        [switch]$long,
        [switch]$size,
        [switch]$all,
        [switch]$help
    )

    if ($h -or $help) {
        Write-Host "Usage: dirw [path] [-l|--long] [-s|--size] [-a|--all] [-f pattern] [-h|--help]" -ForegroundColor Cyan
        Write-Host ""
        Write-Host "Options:"
        Write-Host " path Path to the folder to be displayed (default: . )"
        Write-Host " -l, --long Output sizes/dates (like 'DIR /W' command)."
        Write-Host " -s, --size Calculate size of folders (works with -l, may be slow on large folders and network drives)."
        Write-Host " -a, --all Show hidden files and folders."
        Write-Host " -f Filter by pattern, for example: -f *.cs"
        Write-Host " -h, --help Show this help"
        # Write-Host "Ver. 1.0" -ForegroundColor Gray
        return
    }

    $Filter = if ($f) { $f } else { "*" }
    $Long = $l -or $long
    $ShowHidden = $a -or $all
    $CalculateFolderSize = $s -or $size

    if (-not (Test-Path $Target)) {
        Write-Host "Path not found: $Target" -ForegroundColor Red
        return
    }

    $items = if ($ShowHidden) {
        Get-ChildItem -LiteralPath $Target -Filter $Filter -Force
    } else {
        Get-ChildItem -LiteralPath $Target -Filter $Filter | Where-Object { -not $_.Attributes.ToString().Contains("Hidden") }
    }

    if (-not $items) {
        Write-Host "(nothing found)"
        return
    }

    $execExtensions = @('.exe', '.bat', '.cmd', '.ps1', '.msi')

if ($Long) {
    $maxNameLength = ($items | ForEach-Object { $_.Name.Length } | Measure-Object -Maximum).Maximum
    $maxSizeLength = 10
    $maxDateLength = 19

    $rows = @()
    foreach ($item in $items) {
        $name = $item.Name.PadRight($maxNameLength)
        $color = "White"
        if ($item.PSIsContainer) {
            if ($CalculateFolderSize) {
                $folderBytes = Get-FolderSize $item.FullName
                $sizeVal = Format-FileSize($folderBytes)
            } else {
                $sizeVal = "<DIR>"
            }
            $color = "Yellow"
        } else {
            $sizeVal = Format-FileSize($item.Length)
            if ($execExtensions -contains $item.Extension.ToLower()) {
                $color = "Green"
            }
        }
        $sizeText = $sizeVal.PadLeft($maxSizeLength)
        $date = $item.LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss")
        $rowText = "$name $sizeText $date"
        $rows += [PSCustomObject]@{ Text = $rowText; Color = $color }
    }

    $header = ("Name".PadRight($maxNameLength) + " " + "Size".PadLeft($maxSizeLength) + " " + "Modified")
    $maxRowLength = ($rows | ForEach-Object { $_.Text.Length } | Measure-Object -Maximum).Maximum
    $consoleWidth = [console]::WindowWidth
    $spacing = 4
    $columnWidth = $maxRowLength + $spacing
    $columns = [Math]::Max(1, [Math]::Floor($consoleWidth / $columnWidth))

    # Печатаем заголовок над каждой колонкой
    $headerLine = ""
    for ($c = 0; $c -lt $columns; $c++) {
        $headerLine += $header.PadRight($columnWidth)
    }
    Write-Host $headerLine -ForegroundColor Cyan

    # Выводим в несколько колонок
    for ($i = 0; $i -lt $rows.Count; $i += $columns) {
        for ($j = 0; $j -lt $columns; $j++) {
            $index = $i + $j
            if ($index -lt $rows.Count) {
                $row = $rows[$index]
                Write-Host -NoNewline $row.Text.PadRight($columnWidth) -ForegroundColor $row.Color
            }
        }
        Write-Host ""
    }
}
    else {
        # краткий вывод в колонках
        $maxNameLength = ($items | ForEach-Object { $_.Name.Length } | Measure-Object -Maximum).Maximum
        $consoleWidth = [console]::WindowWidth
        $spacing = 2
        $columnWidth = $maxNameLength + $spacing
        $columns = [Math]::Max(1, [Math]::Floor($consoleWidth / $columnWidth))

        $i = 0
        foreach ($item in $items) {
            $name = $item.Name
            $padded = $name.PadRight($columnWidth)

            if ($item.PSIsContainer) {
                $color = 'Yellow'
            }
            elseif ($execExtensions -contains $item.Extension.ToLower()) {
                $color = 'Green'
            }
            else {
                $color = 'White'
            }

            Write-Host -NoNewline $padded -ForegroundColor $color
            $i++
            if ($i -ge $columns) {
                $i = 0
                Write-Host ""
            }
        }

        if ($i -ne 0) { Write-Host "" }
    }
    Write-Host ""
}
Export-ModuleMember -Function dirw