diff --git a/Private/PowerTree/Scanning/Get-TreeSnapshot.ps1 b/Private/PowerTree/Scanning/Get-TreeSnapshot.ps1 index a0f3d15..317335c 100644 --- a/Private/PowerTree/Scanning/Get-TreeSnapshot.ps1 +++ b/Private/PowerTree/Scanning/Get-TreeSnapshot.ps1 @@ -23,13 +23,45 @@ function Get-TreeSnapshot { $hiddenAttributes = [System.IO.FileAttributes]::Hidden -bor [System.IO.FileAttributes]::System $needFullSizes = $TreeConfig.HeaderTable.HeaderColumns -contains "Size" -or ($TreeConfig.SortFolders -and $TreeConfig.SortBy -eq "Size") - $nodes = [System.Collections.Generic.List[object]]::new() + $needPostProcessing = $needFullSizes -or $TreeConfig.PruneEmptyFolders + $sortDirection = if ($TreeConfig.SortDescending) { -1 } else { 1 } + $sortBy = $TreeConfig.SortBy + $fileComparison = [System.Comparison[System.IO.FileInfo]] { + param($left, $right) + + $comparison = switch ($sortBy) { + "Modification Date" { [datetime]::Compare($left.LastWriteTime, $right.LastWriteTime) } + "Creation Date" { [datetime]::Compare($left.CreationTime, $right.CreationTime) } + "Last Access Date" { [datetime]::Compare($left.LastAccessTime, $right.LastAccessTime) } + "Size" { $left.Length.CompareTo($right.Length) } + default { [StringComparer]::CurrentCultureIgnoreCase.Compare($left.Name, $right.Name) } + } + return $comparison * $sortDirection + } + $directoryComparison = [System.Comparison[object]] { + param($left, $right) + + $comparison = switch ($sortBy) { + "Modification Date" { [datetime]::Compare($left.Item.LastWriteTime, $right.Item.LastWriteTime) } + "Creation Date" { [datetime]::Compare($left.Item.CreationTime, $right.Item.CreationTime) } + "Last Access Date" { [datetime]::Compare($left.Item.LastAccessTime, $right.Item.LastAccessTime) } + "Size" { $left.Size.CompareTo($right.Size) } + default { [StringComparer]::CurrentCultureIgnoreCase.Compare($left.Item.Name, $right.Item.Name) } + } + return $comparison * $sortDirection + } + $nodes = $null + if ($needPostProcessing) { + $nodes = [System.Collections.Generic.List[object]]::new() + } $pending = [System.Collections.Generic.Stack[object]]::new() $pending.Push($root) while ($pending.Count -gt 0) { $node = $pending.Pop() - $nodes.Add($node) + if ($needPostProcessing) { + $nodes.Add($node) + } if (-not $node.CanRenderContents -and -not $needFullSizes) { continue @@ -63,7 +95,9 @@ function Get-TreeSnapshot { continue } - $node.DirectSize += $item.Length + if ($needFullSizes) { + $node.DirectSize += $item.Length + } if (-not $node.CanRenderContents) { continue } @@ -110,10 +144,26 @@ function Get-TreeSnapshot { Write-Verbose "Could not enumerate '$($node.Item.FullName)': $($_.Exception.Message)" } + if ($files.Count -gt 1) { + $files.Sort($fileComparison) + } + if ($TreeConfig.FileLimit -eq 0) { + $files.Clear() + } elseif ($TreeConfig.FileLimit -gt 0 -and $files.Count -gt $TreeConfig.FileLimit) { + $files.RemoveRange($TreeConfig.FileLimit, $files.Count - $TreeConfig.FileLimit) + } + if ($TreeConfig.SortFolders -and $TreeConfig.SortBy -ne "Size" -and $directories.Count -gt 1) { + $directories.Sort($directoryComparison) + } + $node.Files = $files.ToArray() $node.Directories = $directories.ToArray() } + if (-not $needPostProcessing) { + return $root + } + $nodeArray = $nodes.ToArray() [array]::Reverse($nodeArray) @@ -129,27 +179,11 @@ function Get-TreeSnapshot { $node.HasVisibleContent = $true } - if ($node.Files.Count -gt 0) { - $node.Files = @(Group-Items -Items $node.Files ` - -SortBy $TreeConfig.SortBy ` - -SortDescending $TreeConfig.SortDescending) - - if ($TreeConfig.FileLimit -eq 0) { - $node.Files = @() - } elseif ($TreeConfig.FileLimit -gt 0 -and $node.Files.Count -gt $TreeConfig.FileLimit) { - $node.Files = @($node.Files[0..($TreeConfig.FileLimit - 1)]) - } - } - - if ($TreeConfig.SortFolders -and $node.Directories.Count -gt 0) { - $property = switch ($TreeConfig.SortBy) { - "Modification Date" { { $_.Item.LastWriteTime } } - "Creation Date" { { $_.Item.CreationTime } } - "Last Access Date" { { $_.Item.LastAccessTime } } - "Size" { { $_.Size } } - default { { $_.Item.Name } } - } - $node.Directories = @($node.Directories | Sort-Object -Property $property -Descending:$TreeConfig.SortDescending) + if ($TreeConfig.SortFolders -and $TreeConfig.SortBy -eq "Size" -and $node.Directories.Count -gt 1) { + $directories = [System.Collections.Generic.List[object]]::new() + $directories.AddRange($node.Directories) + $directories.Sort($directoryComparison) + $node.Directories = $directories.ToArray() } } diff --git a/Private/PowerTree/Sorting/Get-SortingMethod.ps1 b/Private/PowerTree/Sorting/Get-SortingMethod.ps1 index 716d8f8..e53b928 100644 --- a/Private/PowerTree/Sorting/Get-SortingMethod.ps1 +++ b/Private/PowerTree/Sorting/Get-SortingMethod.ps1 @@ -30,31 +30,3 @@ function Get-SortingMethod{ return $sortBy } - -# Function to sort items based on specified criteria -function Group-Items { - param ( - [Parameter(Mandatory=$false)] - [System.Object[]]$Items, - - [Parameter(Mandatory=$true)] - [string]$SortBy, - - [Parameter()] - [bool]$SortDescending = $false - ) - - if ($null -eq $Items -or $Items.Count -eq 0) { - return @() - } - - $property = switch ($SortBy) { - "Modification Date" { "LastWriteTime" } - "Creation Date" { "CreationTime" } - "Last Access Date" { "LastAccessTime" } - "Size" { "Length" } - default { "Name" } - } - - return $Items | Sort-Object -Property $property -Descending:$SortDescending -} diff --git a/Tests/PowerTree.Core.Tests.ps1 b/Tests/PowerTree.Core.Tests.ps1 index 91f99dd..dfe2093 100644 --- a/Tests/PowerTree.Core.Tests.ps1 +++ b/Tests/PowerTree.Core.Tests.ps1 @@ -145,15 +145,5 @@ Describe "sorting helpers" { Get-SortingMethod -SortByCreationDate $true -DefaultSort Name | Should -Be "Creation Date" Get-SortingMethod -DefaultSort "Modification Date" | Should -Be "Modification Date" } - - It "groups items by name, size, and direction" { - $items = @( - [pscustomobject]@{ Name = "b"; Length = 1 }, - [pscustomobject]@{ Name = "a"; Length = 3 } - ) - @(Group-Items $items Name).Name | Should -Be @("a", "b") - @(Group-Items $items Size $true).Name | Should -Be @("a", "b") - @(Group-Items @() Name).Count | Should -Be 0 - } } } diff --git a/Tests/PowerTree.Integration.Tests.ps1 b/Tests/PowerTree.Integration.Tests.ps1 index 22984cc..a0504b5 100644 --- a/Tests/PowerTree.Integration.Tests.ps1 +++ b/Tests/PowerTree.Integration.Tests.ps1 @@ -38,6 +38,7 @@ try { [void][System.IO.Directory]::CreateDirectory((Join-Path $fixturePath "beta/nested")) [void][System.IO.Directory]::CreateDirectory((Join-Path $fixturePath "empty")) [System.IO.File]::WriteAllBytes((Join-Path $fixturePath "root.md"), [byte[]]::new(2)) + [System.IO.File]::WriteAllBytes((Join-Path $fixturePath "z.md"), [byte[]]::new(7)) $hiddenFilePath = Join-Path $fixturePath ".secret.txt" [System.IO.File]::WriteAllBytes($hiddenFilePath, [byte[]]::new(4)) if ($IsWindows) { @@ -94,6 +95,26 @@ try { Assert-Match $depthOutput "alpha" "depth directories" Assert-NotMatch $depthOutput "a\.txt" "depth files" + $nameSortOutput = Invoke-TestTree "name-sort" @{ + Depth = 1 + SortByName = $true + Descending = $true + } + if ($nameSortOutput.IndexOf("z.md") -gt $nameSortOutput.IndexOf("root.md")) { + throw "descending name sorting failed" + } + $passed++ + + $fileSizeSortOutput = Invoke-TestTree "file-size-sort" @{ + Depth = 1 + SortBySize = $true + Descending = $true + } + if ($fileSizeSortOutput.IndexOf("z.md") -gt $fileSizeSortOutput.IndexOf("root.md")) { + throw "descending file size sorting failed" + } + $passed++ + $filteredOutput = Invoke-TestTree "filtered" @{ IncludeExtensions = @("txt") PruneEmptyFolders = $true