├── .github └── workflows │ ├── PowerColorLS-CI.yml │ └── powershell-analysis.yml ├── LICENSE.txt ├── README.md ├── ReloadModule.ps1 ├── RunTests.ps1 ├── Tests ├── FileAndFolderFunctions.Tests.ps1 ├── Get-Color.Tests.ps1 ├── Get-CommandExist.Tests.ps1 ├── Get-OptionsResult.Tests.ps1 ├── LongFormatHelper.Tests.ps1 ├── ModuleShouldLoad.Tests.ps1 ├── ScriptAnalyzerShoulsPassWithNoErrors..Tests.ps1 ├── SharedMocks.ps1 └── Show-Report.Tests.ps1 ├── media └── screens │ ├── git.png │ ├── powercolorls.png │ ├── powercolorls_a.png │ ├── powercolorls_a_l.png │ ├── powercolorls_a_l_show_directory_size.png │ ├── powercolorls_f_d.png │ └── powercolorls_obj_l.png └── src ├── Init └── Import-Dependencies.ps1 ├── PowerColorLS.psd1 ├── PowerColorLS.psm1 └── Private ├── FileAndFolderFunctions.ps1 ├── Get-Color.ps1 ├── Get-CommandExist.ps1 ├── Get-Icon.ps1 ├── Get-LongestItem.ps1 ├── Get-OptionsResult.ps1 ├── GitFunctions.ps1 ├── InfoFunctions.ps1 ├── LongFormatHelper.ps1 ├── Show-Report.ps1 └── Splat.ps1 /.github/workflows/PowerColorLS-CI.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI 4 | 5 | # Controls when the action will run. Triggers the workflow on push or pull request 6 | # events but only for the master branch 7 | on: 8 | push: 9 | branches: [ master ] 10 | pull_request: 11 | branches: [ master ] 12 | 13 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 14 | jobs: 15 | # This workflow contains a single job called "build" 16 | build: 17 | # The type of runner that the job will run on 18 | runs-on: windows-latest 19 | 20 | # Steps represent a sequence of tasks that will be executed as part of the job 21 | steps: 22 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 23 | - uses: actions/checkout@v2 24 | 25 | # Install Pester testing framework 26 | - name: Install Pester 27 | run: Install-Module -Name Pester -Force 28 | shell: pwsh 29 | 30 | # Install Script Analyzer 31 | - name: Install Script Analyzer 32 | run: Install-Module -Name PSScriptAnalyzer -Force 33 | shell: pwsh 34 | 35 | 36 | # Install prerequisite Terminal-Icons module 37 | - name: Install Terminal-Icons 38 | run: Install-Module -Name Terminal-Icons -Repository PSGallery -Force 39 | shell: pwsh 40 | 41 | # Runs a single command using the runners shell 42 | - name: Run Pester Tests 43 | run: .\RunTests.ps1 44 | shell: pwsh 45 | 46 | # Runs a set of commands using the runners shell 47 | #- name: Run a multi-line script 48 | # run: | 49 | # echo Add other actions to build, 50 | # echo test, and deploy your project. 51 | -------------------------------------------------------------------------------- /.github/workflows/powershell-analysis.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | # 6 | # https://github.com/microsoft/action-psscriptanalyzer 7 | # For more information on PSScriptAnalyzer in general, see 8 | # https://github.com/PowerShell/PSScriptAnalyzer 9 | 10 | name: PSScriptAnalyzer 11 | 12 | on: 13 | push: 14 | branches: [ main ] 15 | pull_request: 16 | branches: [ main ] 17 | schedule: 18 | - cron: '20 5 * * 3' 19 | 20 | jobs: 21 | build: 22 | name: PSScriptAnalyzer 23 | runs-on: ubuntu-latest 24 | steps: 25 | - uses: actions/checkout@v2 26 | 27 | - name: Run PSScriptAnalyzer 28 | uses: microsoft/psscriptanalyzer-action@2044ae068e37d0161fa2127de04c19633882f061 29 | with: 30 | # Check https://github.com/microsoft/action-psscriptanalyzer for more info about the options. 31 | # The below set up runs PSScriptAnalyzer to your entire repository and runs some basic security rules. 32 | path: .\ 33 | recurse: true 34 | # Include your own basic security rules. Removing this option will run all the rules 35 | includeRule: '"PSAvoidGlobalAliases", "PSAvoidUsingConvertToSecureStringWithPlainText"' 36 | output: results.sarif 37 | 38 | # Upload the SARIF file generated in the previous step 39 | - name: Upload SARIF results file 40 | uses: github/codeql-action/upload-sarif@v1 41 | with: 42 | sarif_file: results.sarif 43 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Anders Gardebring 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PowerColorLS 2 | 3 | A PowerShell module that displays a colorized directory and file listing with icons. Inspired by [colorls](https://github.com/athityakumar/colorls) 4 | 5 | ![Screenshot 1](./media/screens/powercolorls.png) 6 | 7 | ## Overview 8 | 9 | *PowerColorLS* is a PowerShell module that displays a colorized directory and file listing with icons in the terminal. 10 | For the module to work, you must first install [Terminal-Icons](https://github.com/devblackops/Terminal-Icons/) and setup the [Nerd Fonts](https://github.com/ryanoasis/nerd-fonts/) 11 | 12 | ## Installation 13 | To install the module from the [PowerShell Gallery](https://www.powershellgallery.com/): 14 | ```powershell 15 | Install-Module -Name PowerColorLS -Repository PSGallery 16 | ``` 17 | 18 | ## Example usage 19 | ```powershell 20 | Import-Module PowerColorLS 21 | PowerColorLS 22 | ``` 23 | 24 | 25 | List all files and directories including hidden ones and ones starting with '.' in long listing (wide) format 26 | ```powershell 27 | PowerColorLS -a -l 28 | ``` 29 | ![Screenshot 2](./media/screens/powercolorls_a_l.png) 30 | 31 | List all files and directories including hidden ones and ones starting with '.' in long listing (wide) format and also include directory size 32 | ``` 33 | PowerColorLS -a -l --show-directory-size 34 | ``` 35 | ![Screenshot 3](./media/screens/powercolorls_a_l_show_directory_size.png) 36 | 37 | List files only, followed by list directories only 38 | ``` 39 | PowerColorLS -f 40 | PowerColorLS -d 41 | ``` 42 | ![Screenshot 4](./media/screens/powercolorls_f_d.png) 43 | 44 | Listing files in a git directory displays git status as well 45 | ``` 46 | PowerColorLS 47 | ``` 48 | ![Screenshot 5](./media/screens/git.png) 49 | 50 | ## Help 51 | To get help about available arguments, run: 52 | ```powershell 53 | PowerColorLS --help 54 | ``` 55 | 56 | Output of help command: 57 | ``` 58 | Usage: PowerColorLs [OPTION]... [FILE]... 59 | List information about files and directories (the current directory by default). 60 | Entries will be sorted alphabetically if no sorting option is specified. 61 | 62 | -a, --all do not ignore hidden files and files starting with . 63 | -l, --long use a long listing format 64 | -r, --report shows a brief report 65 | -1 list one file per line 66 | -d, --dirs show only directories 67 | -f, --files show only files 68 | -ds, -sds, --sds, --show-directory-size 69 | show directory size (can take a long time) 70 | -hi, --hide-icons hide icons 71 | 72 | sorting options: 73 | 74 | -sd, --sort-dirs, --group-directories-first 75 | sort directories first 76 | -sf, --sort-files, --group-files-first 77 | sort files first 78 | -t, -st, --st 79 | sort by modification time, newest first 80 | 81 | general options: 82 | 83 | -h, --help prints this help 84 | -v, --version show version information 85 | ``` 86 | 87 | ## Alias to ls 88 | ```powershell 89 | Set-Alias -Name ls -Value PowerColorLS -Option AllScope 90 | ``` -------------------------------------------------------------------------------- /ReloadModule.ps1: -------------------------------------------------------------------------------- 1 | Remove-Module PowerColorLS 2 | Import-Module .\src\PowerColorLS.psd1 3 | PowerColorLS 4 | Write-Host "======" 5 | PowerColorLS -l -a -sds -r 6 | -------------------------------------------------------------------------------- /RunTests.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | [string]$scriptFile 3 | ) 4 | 5 | $runCoverage = $false 6 | 7 | if("" -eq $scriptFile){ 8 | $scriptFile = ".\Tests" 9 | $runCoverage = $true 10 | } 11 | 12 | Import-Module Pester 13 | Set-StrictMode -Version Latest 14 | 15 | if($runCoverage){ 16 | $Results = Invoke-Pester -PassThru -CodeCoverage "src/Private/*.ps1" $scriptFile 17 | }else{ 18 | $Results = Invoke-Pester -PassThru $scriptFile 19 | } 20 | -------------------------------------------------------------------------------- /Tests/FileAndFolderFunctions.Tests.ps1: -------------------------------------------------------------------------------- 1 | $ROOT = Split-Path -Parent $MyInvocation.MyCommand.Path 2 | 3 | .$ROOT/../src/Private/FileAndFolderFunctions.ps1 4 | 5 | . $PSScriptRoot/SharedMocks.ps1 6 | 7 | # internal help functions 8 | function Get-MockedGetChildItem{ 9 | param($Path) 10 | 11 | $file1 = New-Object -TypeName System.IO.FileInfo -ArgumentList ".dotfile" 12 | $directory1 = New-Object -TypeName System.IO.DirectoryInfo -ArgumentList ".git" 13 | $directory2 = New-Object -TypeName System.IO.DirectoryInfo -ArgumentList "a-dir4" 14 | $file2 = New-Object -TypeName System.IO.FileInfo -ArgumentList "b-file2" 15 | $directory3 = New-Object -TypeName System.IO.DirectoryInfo -ArgumentList "dir2.dirext" 16 | $directory4 = New-Object -TypeName System.IO.DirectoryInfo -ArgumentList "dir3.dirext" 17 | $file3 = New-Object -TypeName System.IO.FileInfo -ArgumentList "file3.doc" 18 | $file4 = New-Object -TypeName System.IO.FileInfo -ArgumentList "file4.txt" 19 | $file5 = New-Object -TypeName System.IO.FileInfo -ArgumentList "file5.txt" 20 | 21 | $fakeListingOfDirectories = @($directory1, $directory2, $directory3, $directory4, $file1, $file2, $file3, $file4, $file5) 22 | 23 | if($Path -ne "."){ 24 | $fakeListingOfDirectories = @($fakeListingOfDirectories | Where-Object Name -Like $Path) 25 | } 26 | 27 | return [array]$fakeListingOfDirectories 28 | } 29 | 30 | # tests 31 | 32 | Describe "FileAndFolder Functions Tests" { 33 | BeforeAll { 34 | Mock -CommandName Get-ChildItem -MockWith { Get-MockedGetChildItem -Path $Path } 35 | 36 | $countTestCases = @( 37 | @{ 38 | testName = "count when not including hidden files and directories" 39 | options = Get-MockedOptions 40 | expectedLength = 7 41 | } 42 | @{ 43 | testName = "count when including hidden files and directories" 44 | options = (Get-MockedOptions -adjustments @{showHiddenFiles = $true}) 45 | expectedLength = 9 46 | } 47 | @{ 48 | testName = "file count when not including hidden files" 49 | options = (Get-MockedOptions -adjustments @{fileOnly = $true}) 50 | expectedLength = 4 51 | } 52 | @{ 53 | testName = "file count when including hidden files" 54 | options = (Get-MockedOptions -adjustments @{showHiddenFiles = $true; fileOnly = $true}) 55 | expectedLength = 5 56 | } 57 | @{ 58 | testName = "directory count when not including hidden directories" 59 | options = (Get-MockedOptions -adjustments @{dirOnly = $true}) 60 | expectedLength = 3 61 | } 62 | @{ 63 | testName = "directory count when including hidden directories" 64 | options = (Get-MockedOptions -adjustments @{showHiddenFiles = $true; dirOnly = $true}) 65 | expectedLength = 4 66 | } 67 | @{ 68 | testName = "count when using query filter and not including hidden files and directories" 69 | options = Get-MockedOptions 70 | expectedLength = 2 71 | query = "*.txt" 72 | } 73 | @{ 74 | testName = "count when using query filter and not including hidden files and directories" 75 | options = Get-MockedOptions 76 | expectedLength = 1 77 | query = "*.doc" 78 | } 79 | @{ 80 | testName = "count when using query filter and including hidden files and directories" 81 | options = (Get-MockedOptions -adjustments @{showHiddenFiles = $true; }) 82 | expectedLength = 1 83 | query = ".dotfile" 84 | } 85 | @{ 86 | testName = "count when using query filter and including hidden files and directories" 87 | options = (Get-MockedOptions -adjustments @{showHiddenFiles = $true; }) 88 | expectedLength = 2 89 | query = ".*" 90 | } 91 | ) 92 | 93 | $orderTestCases = @( 94 | @{ 95 | testName = "default sorting and not showing hidden files and directories" 96 | options = Get-MockedOptions 97 | expectedfirstFileName = "a-dir4" 98 | } 99 | @{ 100 | testName = "default sorting and showing hidden files and directories" 101 | options = (Get-MockedOptions -adjustments @{showHiddenFiles = $true}) 102 | expectedfirstFileName = ".dotfile" 103 | } 104 | @{ 105 | testName = "default sorting and not showing hidden files" 106 | options = (Get-MockedOptions -adjustments @{fileOnly = $true}) 107 | expectedfirstFileName = "b-file2" 108 | } 109 | @{ 110 | testName = "default sorting and not showing hidden files" 111 | options = (Get-MockedOptions -adjustments @{filesFirst = $true}) 112 | expectedfirstFileName = "b-file2" 113 | } 114 | @{ 115 | testName = "default sorting and showing hidden files" 116 | options = (Get-MockedOptions -adjustments @{showHiddenFiles = $true; fileOnly = $true}) 117 | expectedfirstFileName = ".dotfile" 118 | } 119 | @{ 120 | testName = "default sorting and not showing hidden directories" 121 | options = (Get-MockedOptions -adjustments @{dirOnly = $true}) 122 | expectedfirstFileName = "a-dir4" 123 | } 124 | @{ 125 | testName = "default sorting and not showing hidden directories" 126 | options = (Get-MockedOptions -adjustments @{dirsFirst = $true}) 127 | expectedfirstFileName = "a-dir4" 128 | } 129 | @{ 130 | testName = "default sorting and showing hidden directories" 131 | options = (Get-MockedOptions -adjustments @{showHiddenFiles = $true; dirOnly = $true}) 132 | expectedfirstFileName = ".git" 133 | } 134 | @{ 135 | testName = "default sorting and query filter and not showing hidden directories" 136 | options = Get-MockedOptions 137 | expectedfirstFileName = "file4.txt" 138 | query = "*.txt" 139 | } 140 | @{ 141 | testName = "default sorting and query filter and showing hidden directories" 142 | options = (Get-MockedOptions -adjustments @{showHiddenFiles = $true;}) 143 | expectedfirstFileName = ".dotfile" 144 | query = ".*" 145 | } 146 | ) 147 | } 148 | 149 | Context "When Getting Friendly Size" { 150 | It "Should return 1 KB for 1024 bytes" { 151 | $friendlySize = Get-FriendlySize -bytes 1024 152 | $friendlySize | Should be "1 KB" 153 | } 154 | 155 | It "Should return 123 B for 123 bytes" { 156 | $friendlySize = Get-FriendlySize -bytes 123 157 | $friendlySize | Should be "123 B" 158 | } 159 | } 160 | 161 | Context "When Listing Files" { 162 | It "Should return expected " -TestCases $countTestCases { 163 | param($options, $expectedLength, $query = ".") 164 | [array]$filesListing = (Get-FilesAndFoldersListing -options $options -query $query) 165 | $filesListing.Length | Should Be $expectedLength 166 | } 167 | 168 | It "Should return expected sort order when using " -TestCases $orderTestCases { 169 | param($options, $expectedfirstFileName, $query = ".") 170 | [array]$filesListing = Get-FilesAndFoldersListing -options $options -query $query 171 | $filesListing[0].Name | Should Be $expectedfirstFileName 172 | } 173 | } 174 | } 175 | 176 | Describe "FileAndFolder Functions Tests" { 177 | 178 | BeforeAll { 179 | Mock -CommandName Get-ChildItem -MockWith { 180 | return Get-MockedFileAndDirectoryListing 181 | } 182 | } 183 | 184 | Context "When Getting directory name" { 185 | It "Should return directory name for file" { 186 | $options = Get-MockedOptions -adjustments @{fileOnly = $true;} 187 | $query = "." 188 | [array]$filesAndFolders = (Get-FilesAndFoldersListing -options $options -query $query) 189 | $directoryName = Get-DirectoryName -filesAndFolders $filesAndFolders 190 | $directoryName | Should be $MockedDirectoryName 191 | } 192 | 193 | It "Should return directory name for directory" { 194 | $options = Get-MockedOptions -adjustments @{dirOnly = $true;} 195 | $query = "." 196 | [array]$filesAndFolders = (Get-FilesAndFoldersListing -options $options -query $query) 197 | $directoryName = Get-DirectoryName -filesAndFolders $filesAndFolders 198 | $directoryName | Should be $MockedDirectoryName 199 | } 200 | } 201 | 202 | 203 | Context "When Listing Files" { 204 | It "Should return expected sort order when sorting by last modified datetime"{ 205 | $options = Get-MockedOptions -adjustments @{sortByModificationTime = $true} 206 | $query = "." 207 | [array]$filesListing = Get-FilesAndFoldersListing -options $options -query $query 208 | $filesListing[0].Name | Should Be "file1.txt" 209 | $filesListing[1].Name | Should Be "directory1" 210 | $filesListing[2].Name | Should Be "file2.txt" 211 | $filesListing[3].Name | Should Be "file3.txt" 212 | $filesListing[4].Name | Should Be "directory2" 213 | } 214 | 215 | It "Should return expected sort order when using default sorting"{ 216 | $options = Get-MockedOptions 217 | $query = "." 218 | [array]$filesListing = Get-FilesAndFoldersListing -options $options -query $query 219 | $filesListing[0].Name | Should Be "directory1" 220 | $filesListing[1].Name | Should Be "directory2" 221 | $filesListing[2].Name | Should Be "file1.txt" 222 | $filesListing[3].Name | Should Be "file2.txt" 223 | $filesListing[4].Name | Should Be "file3.txt" 224 | } 225 | 226 | It "Should return expected sort order when sorting files first"{ 227 | $options = Get-MockedOptions -adjustments @{filesFirst = $true} 228 | $query = "." 229 | [array]$filesListing = Get-FilesAndFoldersListing -options $options -query $query 230 | $filesListing[0].Name | Should Be "file1.txt" 231 | $filesListing[1].Name | Should Be "file2.txt" 232 | $filesListing[2].Name | Should Be "file3.txt" 233 | $filesListing[3].Name | Should Be "directory1" 234 | $filesListing[4].Name | Should Be "directory2" 235 | } 236 | 237 | It "Should return expected sort order when sorting directories first"{ 238 | $options = Get-MockedOptions -adjustments @{dirsFirst = $true} 239 | $query = "." 240 | [array]$filesListing = Get-FilesAndFoldersListing -options $options -query $query 241 | $filesListing[0].Name | Should Be "directory1" 242 | $filesListing[1].Name | Should Be "directory2" 243 | $filesListing[2].Name | Should Be "file1.txt" 244 | $filesListing[3].Name | Should Be "file2.txt" 245 | $filesListing[4].Name | Should Be "file3.txt" 246 | } 247 | } 248 | } 249 | 250 | Describe "Get-LongestItemLength Functions Tests" { 251 | Context "When getting longest item" { 252 | It "Should return the expected longest item for file"{ 253 | Mock -CommandName Get-ChildItem -MockWith { Get-MockedGetChildItem -Path $Path } 254 | $options = Get-MockedOptions @{fileOnly = $true} 255 | $query = "." 256 | [array]$filesListing = Get-FilesAndFoldersListing -options $options -query $query 257 | $li = Get-LongestItemLength -filesAndFolders $filesListing 258 | $li | Should Be 9 259 | } 260 | It "Should return the expected longest item for directory"{ 261 | Mock -CommandName Get-ChildItem -MockWith { Get-MockedGetChildItem -Path $Path } 262 | $options = Get-MockedOptions @{dirOnly = $true} 263 | $query = "." 264 | [array]$filesListing = Get-FilesAndFoldersListing -options $options -query $query 265 | $li = Get-LongestItemLength -filesAndFolders $filesListing 266 | $li | Should Be 11 267 | } 268 | } 269 | } 270 | 271 | Describe "Get-NameForDisplay Functions Tests" { 272 | BeforeAll { 273 | Mock -CommandName Get-ChildItem -MockWith { Get-MockedGetChildItem -Path $Path } 274 | } 275 | Context "When getting name for display" { 276 | It "Should return the expected name for file"{ 277 | $options = Get-MockedOptions 278 | $query = "." 279 | [array]$filesListing = Get-FilesAndFoldersListing -options $options -query $query 280 | $nameForDisplay = Get-NameForDisplay -fileSystemInfo $filesListing[1] 281 | $nameForDisplay | Should Be "b-file2" 282 | } 283 | 284 | It "Should return the expected name for directory"{ 285 | $options = Get-MockedOptions 286 | $query = "." 287 | [array]$filesListing = Get-FilesAndFoldersListing -options $options -query $query 288 | $nameForDisplay = Get-NameForDisplay -fileSystemInfo $filesListing[0] 289 | $nameForDisplay | Should Be "a-dir4\" 290 | } 291 | } 292 | } 293 | 294 | Describe "Get-FileExtension Functions Tests" { 295 | Context "When getting file extension for file" { 296 | It "Should return the expected extension when filename has extension"{ 297 | $ext = Get-FileExtension -fileName "file1.txt" 298 | $ext | Should be ".txt" 299 | } 300 | 301 | It "Should return no extension when filename has no extension"{ 302 | $ext = Get-FileExtension -fileName "file1" 303 | $ext | Should be "" 304 | } 305 | } 306 | } 307 | 308 | Describe "Get-IsDirectory Functions Tests" { 309 | Context "When running Get-IsDirectory" { 310 | It "Should return true for a directory"{ 311 | $dir = Get-MockedDirectoryInfo -Name "directory" 312 | $isDir = Get-IsDirectory -fileSystemInfo $dir 313 | $isDir | Should be $true 314 | } 315 | 316 | It "Should return false for a file"{ 317 | $file = Get-MockedFileInfo -file "file1.txt" 318 | $isDir = Get-IsDirectory -fileSystemInfo $file 319 | $isDir | Should be $false 320 | } 321 | } 322 | } -------------------------------------------------------------------------------- /Tests/Get-Color.Tests.ps1: -------------------------------------------------------------------------------- 1 | $ROOT = Split-Path -Parent $MyInvocation.MyCommand.Path 2 | 3 | .$ROOT/../src/Private/Get-Color.ps1 4 | .$ROOT/../src/Private/FileAndFolderFunctions.ps1 5 | 6 | . $PSScriptRoot/SharedMocks.ps1 7 | 8 | function Get-MockedColorTheme{ 9 | return @{ 10 | Types = @{ 11 | Directories = @{ 12 | WellKnown = @{ 13 | '.git' = 'FF4500' 14 | } 15 | } 16 | Files = @{ 17 | WellKnown = @{ 18 | '.gitignore' = 'FF4500' 19 | 'LICENSE.md' = 'CD5C5C' 20 | } 21 | '.zip' = 'DAA520' 22 | '.xml' = '98FB98' 23 | } 24 | } 25 | } 26 | 27 | } 28 | 29 | 30 | Describe "Get-Color Functions Tests" { 31 | BeforeAll { 32 | } 33 | Context "When getting color" { 34 | It "Should return expected color for well known directory"{ 35 | $dir = Get-MockedDirectoryInfo -name ".git" 36 | $colorTheme = Get-MockedColorTheme 37 | $expectedColor = ConvertFrom-RGBColor -RGB ("FF4500") 38 | $color = Get-Color -fileSystemInfo $dir -colorTheme $colorTheme 39 | $color | Should be $expectedColor 40 | } 41 | 42 | It "Should return expected color for not well known directory"{ 43 | $dir = Get-MockedDirectoryInfo -name "directoryname" 44 | $colorTheme = Get-MockedColorTheme 45 | $expectedColor = ConvertFrom-RGBColor -RGB ("EEEE8B") 46 | $color = Get-Color -fileSystemInfo $dir -colorTheme $colorTheme 47 | $color | Should be $expectedColor 48 | } 49 | 50 | It "Should return expected color for well known file"{ 51 | $dir = Get-MockedFileInfo -name ".gitignore" 52 | $colorTheme = Get-MockedColorTheme 53 | $expectedColor = ConvertFrom-RGBColor -RGB ("FF4500") 54 | $color = Get-Color -fileSystemInfo $dir -colorTheme $colorTheme 55 | $color | Should be $expectedColor 56 | } 57 | 58 | It "Should return expected color for known file extension"{ 59 | $dir = Get-MockedFileInfo -name "test.zip" 60 | $colorTheme = Get-MockedColorTheme 61 | $expectedColor = ConvertFrom-RGBColor -RGB ("DAA520") 62 | $color = Get-Color -fileSystemInfo $dir -colorTheme $colorTheme 63 | $color | Should be $expectedColor 64 | } 65 | 66 | It "Should return expected color for not known file extension"{ 67 | $dir = Get-MockedFileInfo -name "test.unknown" 68 | $colorTheme = Get-MockedColorTheme 69 | $expectedColor = ConvertFrom-RGBColor -RGB ("EEEEEE") 70 | $color = Get-Color -fileSystemInfo $dir -colorTheme $colorTheme 71 | $color | Should be $expectedColor 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Tests/Get-CommandExist.Tests.ps1: -------------------------------------------------------------------------------- 1 | $ROOT = Split-Path -Parent $MyInvocation.MyCommand.Path 2 | 3 | .$ROOT\..\src\Private\Get-CommandExist.ps1 4 | 5 | Describe "Command Helper Tests" { 6 | Context "When testing for command that does not exist" { 7 | It "Should return false" { 8 | $exists = Get-CommandExist -command "ThisDoesNotExist" 9 | $exists | Should be $False 10 | } 11 | } 12 | 13 | Context "When testing for command that exist" { 14 | It "Should return true" { 15 | $exists = Get-CommandExist -command "git" 16 | $exists | Should be $True 17 | } 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /Tests/Get-OptionsResult.Tests.ps1: -------------------------------------------------------------------------------- 1 | $ROOT = Split-Path -Parent $MyInvocation.MyCommand.Path 2 | 3 | .$ROOT\..\src\Private\Get-OptionsResult.ps1 4 | .$ROOT\..\src\Private\InfoFunctions.ps1 5 | 6 | . $PSScriptRoot/SharedMocks.ps1 7 | 8 | Describe "Get-OptionsResult Tests" { 9 | BeforeAll { 10 | Mock -CommandName Show-Version 11 | Mock -CommandName Show-Help 12 | 13 | $getOptionsTestCases = @( 14 | @{ 15 | testName = "default options when no option is specified" 16 | optionsResult = Get-OptionsResult 17 | expectedOptions = Get-MockedOptions 18 | continue = $true 19 | errorMessage = $null 20 | query = "." 21 | } 22 | @{ 23 | testName = "oneEntryPerLine option when -1 flag is specified" 24 | optionsResult = Get-OptionsResult -arguments "-1" 25 | expectedOptions = Get-MockedOptions -adjustments @{oneEntryPerLine = $true;} 26 | continue = $true 27 | errorMessage = $null 28 | query = "." 29 | } 30 | @{ 31 | testName = "showHiddenFiles option when -a flag is specified" 32 | optionsResult = Get-OptionsResult -arguments "-a" 33 | expectedOptions = Get-MockedOptions -adjustments @{showHiddenFiles = $true;} 34 | continue = $true 35 | errorMessage = $null 36 | query = "." 37 | } 38 | @{ 39 | testName = "dirOnly option when -d flag is specified" 40 | optionsResult = Get-OptionsResult -arguments "-d" 41 | expectedOptions = Get-MockedOptions -adjustments @{dirOnly = $true;} 42 | continue = $true 43 | errorMessage = $null 44 | query = "." 45 | } 46 | @{ 47 | testName = "fileOnly option when -f flag is specified" 48 | optionsResult = Get-OptionsResult -arguments "-f" 49 | expectedOptions = Get-MockedOptions -adjustments @{fileOnly = $true;} 50 | continue = $true 51 | errorMessage = $null 52 | query = "." 53 | } 54 | @{ 55 | testName = "longFormat option when -l flag is specified" 56 | optionsResult = Get-OptionsResult -arguments "-l" 57 | expectedOptions = Get-MockedOptions -adjustments @{longFormat = $true;} 58 | continue = $true 59 | errorMessage = $null 60 | query = "." 61 | } 62 | @{ 63 | testName = "dirsFirst option when -sd flag is specified" 64 | optionsResult = Get-OptionsResult -arguments "-sd" 65 | expectedOptions = Get-MockedOptions -adjustments @{dirsFirst = $true;} 66 | continue = $true 67 | errorMessage = $null 68 | query = "." 69 | } 70 | @{ 71 | testName = "filesFirst option when -sf flag is specified" 72 | optionsResult = Get-OptionsResult -arguments "-sf" 73 | expectedOptions = Get-MockedOptions -adjustments @{filesFirst = $true;} 74 | continue = $true 75 | errorMessage = $null 76 | query = "." 77 | } 78 | @{ 79 | testName = "sortByModificationTime option when -t flag is specified" 80 | optionsResult = Get-OptionsResult -arguments "-t" 81 | expectedOptions = Get-MockedOptions -adjustments @{sortByModificationTime = $true;} 82 | continue = $true 83 | errorMessage = $null 84 | query = "." 85 | } 86 | @{ 87 | testName = "showDirectorySize option when -sds flag is specified" 88 | optionsResult = Get-OptionsResult -arguments "-sds" 89 | expectedOptions = Get-MockedOptions -adjustments @{showDirectorySize = $true;} 90 | continue = $true 91 | errorMessage = $null 92 | query = "." 93 | } 94 | @{ 95 | testName = "showReport option when -r flag is specified" 96 | optionsResult = Get-OptionsResult -arguments "-r" 97 | expectedOptions = Get-MockedOptions -adjustments @{showReport = $true;} 98 | continue = $true 99 | errorMessage = $null 100 | query = "." 101 | } 102 | @{ 103 | testName = "showReport and showDirectorySize option when -r -sds flags are specified" 104 | optionsResult = Get-OptionsResult -arguments "-r -sds".Split(" ") 105 | expectedOptions = Get-MockedOptions -adjustments @{showReport = $true;showDirectorySize = $true;} 106 | continue = $true 107 | errorMessage = $null 108 | query = "." 109 | } 110 | @{ 111 | testName = "default options with expected query when no option and query is specified" 112 | optionsResult = Get-OptionsResult "*.txt" 113 | expectedOptions = Get-MockedOptions 114 | continue = $true 115 | errorMessage = $null 116 | query = "*.txt" 117 | } 118 | @{ 119 | testName = "longFormat option and expected query when --long option and query is specified" 120 | optionsResult = Get-OptionsResult "--long *.md".Split(" ") 121 | expectedOptions = Get-MockedOptions -adjustments @{longFormat = $true;} 122 | continue = $true 123 | errorMessage = $null 124 | query = "*.md" 125 | } 126 | 127 | ) 128 | } 129 | Context "When getting options"{ 130 | It "Should return " -TestCases $getOptionsTestCases { 131 | param($optionsResult, $expectedOptions, $continue, $errorMessage, $query) 132 | $options = $optionsResult.options 133 | $optionsResult.continue | Should be $continue 134 | $optionsResult.errorMessage | Should be $errorMessage 135 | $optionsResult.query | Should be $query 136 | 137 | $options.oneEntryPerLine | Should be $expectedOptions.oneEntryPerLine 138 | $options.showHiddenFiles | Should be $expectedOptions.showHiddenFiles 139 | $options.dirOnly | Should be $expectedOptions.dirOnly 140 | $options.fileOnly | Should be $expectedOptions.fileOnly 141 | $options.longFormat | Should be $expectedOptions.longFormat 142 | $options.dirsFirst | Should be $expectedOptions.dirsFirst 143 | $options.filesFirst | Should be $expectedOptions.filesFirst 144 | $options.sortByModificationTime | Should be $expectedOptions.sortByModificationTime 145 | $options.showDirectorySize | Should be $expectedOptions.showDirectorySize 146 | $options.showReport | Should be $expectedOptions.showReport 147 | } 148 | 149 | It "Should show version when -v flag is specified" { 150 | $optionsResult = (Get-OptionsResult -arguments "-v") 151 | $optionsResult.continue | Should be $false 152 | $optionsResult.errorMessage | Should be $null 153 | Assert-MockCalled Show-Version -Times 1 154 | } 155 | 156 | It "Should show help when -h flag is specified" { 157 | $optionsResult = (Get-OptionsResult -arguments "-h") 158 | $optionsResult.continue | Should be $false 159 | $optionsResult.errorMessage | Should be $null 160 | Assert-MockCalled Show-Help -Times 1 161 | } 162 | 163 | It "Should result in invalid option message when invalid option is passed" { 164 | $optionsResult = (Get-OptionsResult -arguments "-invalidoption") 165 | $optionsResult.continue | Should be $false 166 | $optionsResult.errorMessage | Should be "invalid option -invalidoption" 167 | Assert-MockCalled Show-Help -Times 1 168 | } 169 | 170 | } 171 | } -------------------------------------------------------------------------------- /Tests/LongFormatHelper.Tests.ps1: -------------------------------------------------------------------------------- 1 | $ROOT = Split-Path -Parent $MyInvocation.MyCommand.Path 2 | 3 | .$ROOT\..\src\Private\LongFormatHelper.ps1 4 | .$ROOT\..\src\Private\FileAndFolderFunctions.ps1 5 | .$ROOT\..\src\Private\Get-LongestItem.ps1 6 | .$ROOT\..\src\Private\GitFunctions.ps1 7 | .$ROOT\..\src\Private\Get-CommandExist.ps1 8 | .$ROOT\..\src\Private\Get-Color.ps1 9 | 10 | . $PSScriptRoot/SharedMocks.ps1 11 | 12 | function Get-LongFormatTestData{ 13 | param([int]$availableCharWith) 14 | $options = Get-MockedOptions -adjustments @{longFormat = $true;} 15 | $query = "." 16 | [array]$filesAndFolders = (Get-FilesAndFoldersListing -options $options -query $query) 17 | $longestItemLength = Get-LongestItemLength -filesAndFolders $filesAndFolders 18 | $longFormatData = Get-LongFormatData -options $options -filesAndFolders $filesAndFolders -longestItemLength $longestItemLength 19 | $colorAndIcon = " " 20 | $expectedMode = Get-ModeForLongListing -modeInput $filesAndFolders[0].Mode -hideIcons $false 21 | 22 | $lfp0 = Get-LongFormatPrintout -fileSystemInfo $filesAndFolders[0] -options $options -longFormatData $longFormatData -colorAndIcon $colorAndIcon -availableCharWith $availableCharWith 23 | $lfp2 = Get-LongFormatPrintout -fileSystemInfo $filesAndFolders[2] -options $options -longFormatData $longFormatData -colorAndIcon $colorAndIcon -availableCharWith $availableCharWith 24 | 25 | return @{ 26 | lfp0 = $lfp0 27 | lfp2 = $lfp2 28 | mode0Ok = ($lfp0.Contains($expectedMode)) 29 | mode2Ok = ($lfp2.Contains($expectedMode)) 30 | dt0 = ($filesAndFolders[0].LastWriteTime).ToString("f") 31 | dt2 = ($filesAndFolders[2].LastWriteTime).ToString("f") 32 | } 33 | } 34 | 35 | Describe "LongFormatHelper Functions Tests" { 36 | 37 | BeforeAll { 38 | Mock -CommandName Get-ChildItem -MockWith { 39 | return Get-MockedFileAndDirectoryListing 40 | } 41 | 42 | Mock -CommandName Get-Acl -MockWith { 43 | return Get-MockedAclFromPipeline 44 | } 45 | 46 | Mock -CommandName Get-Item -MockWith { 47 | return Get-MockedItem 48 | } 49 | 50 | Mock -CommandName Get-CommandExist -MockWith { 51 | return $true 52 | } 53 | 54 | Mock -CommandName Write-Host -MockWith { 55 | } 56 | } 57 | 58 | Context "When getting mode for long listing"{ 59 | It "Should return expected mode for mode d----" { 60 | $mode = "d----" 61 | $modeForLongListing = Get-ModeForLongListing -modeInput $mode -hideIcons $false 62 | $color = Get-Mode-Attribute-Color -attribute "d" 63 | $dashcolor = Get-Mode-Attribute-Color -attribute "-" 64 | $modeForLongListing | Should be ("${color}glyph-nf-fa-folder_o ${dashcolor}- ${dashcolor}- ${dashcolor}- ${dashcolor}- ") 65 | } 66 | It "Should return expected mode for mode d--h-" { 67 | $mode = "d--h-" 68 | $modeForLongListing = Get-ModeForLongListing -modeInput $mode -hideIcons $false 69 | $colord = Get-Mode-Attribute-Color -attribute "d" 70 | $colorh = Get-Mode-Attribute-Color -attribute "h" 71 | $dashcolor = Get-Mode-Attribute-Color -attribute "-" 72 | $modeForLongListing | Should be ("${colord}glyph-nf-fa-folder_o ${dashcolor}- ${dashcolor}- ${colorh}glyph-nf-mdi-file_hidden ${dashcolor}- ") 73 | } 74 | 75 | It "Should return expected mode for mode -a---" { 76 | $mode = "-a---" 77 | $modeForLongListing = Get-ModeForLongListing -modeInput $mode -hideIcons $false 78 | $colora = Get-Mode-Attribute-Color -attribute "a" 79 | $dashcolor = Get-Mode-Attribute-Color -attribute "-" 80 | $modeForLongListing | Should be ("${dashcolor}- ${colora}glyph-nf-fa-archive ${dashcolor}- ${dashcolor}- ${dashcolor}- ") 81 | } 82 | 83 | It "Should return expected mode for mode d-r-s" { 84 | $mode = "d-r-s" 85 | $modeForLongListing = Get-ModeForLongListing -modeInput $mode -hideIcons $false 86 | $colord = Get-Mode-Attribute-Color -attribute "d" 87 | $colorr = Get-Mode-Attribute-Color -attribute "r" 88 | $colors = Get-Mode-Attribute-Color -attribute "s" 89 | $dashcolor = Get-Mode-Attribute-Color -attribute "-" 90 | $modeForLongListing | Should be ("${colord}glyph-nf-fa-folder_o ${dashcolor}- ${colorr}glyph-nf-fa-lock ${dashcolor}- ${colors}glyph-nf-fa-gear ") 91 | } 92 | 93 | It "Should return expected mode for mode la---" { 94 | $mode = "la---" 95 | $modeForLongListing = Get-ModeForLongListing -modeInput $mode -hideIcons $false 96 | $colora = Get-Mode-Attribute-Color -attribute "a" 97 | $dashcolor = Get-Mode-Attribute-Color -attribute "-" 98 | $modeForLongListing | Should be ("${dashcolor}l ${colora}glyph-nf-fa-archive ${dashcolor}- ${dashcolor}- ${dashcolor}- ") 99 | } 100 | } 101 | 102 | <# 103 | 104 | la--- 105 | #> 106 | 107 | Context "When getting long format data" { 108 | It "Should return null when not in long format" { 109 | $options = Get-MockedOptions 110 | $query = "." 111 | [array]$filesAndFolders = (Get-FilesAndFoldersListing -options $options -query $query) 112 | $longestItemLength = Get-LongestItemLength -filesAndFolders $filesAndFolders 113 | $longFormatData = Get-LongFormatData -options $options -filesAndFolders $filesAndFolders -longestItemLength $longestItemLength 114 | $longFormatData | Should be $null 115 | } 116 | 117 | It "Should return expected values when in long format" { 118 | $options = Get-MockedOptions -adjustments @{longFormat = $true;} 119 | $query = "." 120 | [array]$filesAndFolders = (Get-FilesAndFoldersListing -options $options -query $query) 121 | $longestItemLength = Get-LongestItemLength -filesAndFolders $filesAndFolders 122 | $longFormatData = Get-LongFormatData -options $options -filesAndFolders $filesAndFolders -longestItemLength $longestItemLength 123 | $longFormatData.longestOwnerAclLength | Should be 10 124 | $longFormatData.longestGroupAclLength | Should be 10 125 | $longFormatData.longestDateLength | Should be 31 126 | $longFormatData.fullItemMaxLength | Should be 95 127 | $longFormatData.noGroupMaxLength | Should be 83 128 | $longFormatData.noGroupOrOwnerMaxLength | Should be 71 129 | $longFormatData.noGroupOrOwnerOrModeMaxLength | Should be 58 130 | } 131 | } 132 | 133 | Context "When Getting long format printout"{ 134 | It "Should return expected printout when at max length"{ 135 | $lftd = Get-LongFormatTestData -availableCharWith 300 136 | $dt0 = $lftd.dt0 137 | $dt2 = $lftd.dt2 138 | 139 | $lftd.mode0Ok | Should -Be $true 140 | $lftd.lfp0 | Should -BeLike "*OWNER\user*" 141 | $lftd.lfp0 | Should -BeLike "*GROUP\user*" 142 | $lftd.lfp0 | Should -BeLike "*${dt0}*" 143 | $lftd.lfp0 | Should -BeLike "*directory1\*" 144 | 145 | $lftd.mode2Ok | Should -Be $true 146 | $lftd.lfp2 | Should -BeLike "*OWNER\user*" 147 | $lftd.lfp2 | Should -BeLike "*GROUP\user*" 148 | $lftd.lfp2 | Should -BeLike "*${dt2}*" 149 | $lftd.lfp2 | Should -BeLike "*file1.txt*" 150 | } 151 | 152 | It "Should return expected printout when at 90 length"{ 153 | $lftd = Get-LongFormatTestData -availableCharWith 90 154 | $dt0 = $lftd.dt0 155 | $dt2 = $lftd.dt2 156 | 157 | $lftd.mode0Ok | Should -Be $true 158 | $lftd.lfp0 | Should -BeLike "*OWNER\user*" 159 | $lftd.lfp0 | Should -Not -BeLike "*GROUP\user*" 160 | $lftd.lfp0 | Should -BeLike "*${dt0}*" 161 | $lftd.lfp0 | Should -BeLike "*directory1\*" 162 | 163 | $lftd.mode2Ok | Should -Be $true 164 | $lftd.lfp2 | Should -BeLike "*OWNER\user*" 165 | $lftd.lfp2 | Should -Not -BeLike "*GROUP\user*" 166 | $lftd.lfp2 | Should -BeLike "*${dt2}*" 167 | $lftd.lfp2 | Should -BeLike "*file1.txt*" 168 | } 169 | 170 | It "Should return expected printout when at 80 length"{ 171 | $lftd = Get-LongFormatTestData -availableCharWith 80 172 | $dt0 = $lftd.dt0 173 | $dt2 = $lftd.dt2 174 | 175 | $lftd.mode0Ok | Should -Be $true 176 | $lftd.lfp0 | Should -Not -BeLike "*OWNER\user*" 177 | $lftd.lfp0 | Should -Not -BeLike "*GROUP\user*" 178 | $lftd.lfp0 | Should -BeLike "*${dt0}*" 179 | $lftd.lfp0 | Should -BeLike "*directory1\*" 180 | 181 | $lftd.mode2Ok | Should -Be $true 182 | $lftd.lfp2 | Should -Not -BeLike "*OWNER\user*" 183 | $lftd.lfp2 | Should -Not -BeLike "*GROUP\user*" 184 | $lftd.lfp2 | Should -BeLike "*${dt2}*" 185 | $lftd.lfp2 | Should -BeLike "*file1.txt*" 186 | } 187 | 188 | It "Should return expected printout when at 70 length"{ 189 | $lftd = Get-LongFormatTestData -availableCharWith 70 190 | $dt0 = $lftd.dt0 191 | $dt2 = $lftd.dt2 192 | 193 | $lftd.mode0Ok | Should -Be $false 194 | $lftd.lfp0 | Should -Not -BeLike "*OWNER\user*" 195 | $lftd.lfp0 | Should -Not -BeLike "*GROUP\user*" 196 | $lftd.lfp0 | Should -BeLike "*${dt0}*" 197 | $lftd.lfp0 | Should -BeLike "*directory1\*" 198 | 199 | $lftd.mode2Ok | Should -Be $false 200 | $lftd.lfp2 | Should -Not -BeLike "*OWNER\user*" 201 | $lftd.lfp2 | Should -Not -BeLike "*GROUP\user*" 202 | $lftd.lfp2 | Should -BeLike "*${dt2}*" 203 | $lftd.lfp2 | Should -BeLike "*file1.txt*" 204 | } 205 | 206 | It "Should return expected printout when at 50 length"{ 207 | $lftd = Get-LongFormatTestData -availableCharWith 50 208 | $dt0 = $lftd.dt0 209 | $dt2 = $lftd.dt2 210 | 211 | $lftd.mode0Ok | Should -Be $false 212 | $lftd.lfp0 | Should -Not -BeLike "*OWNER\user*" 213 | $lftd.lfp0 | Should -Not -BeLike "*GROUP\user*" 214 | $lftd.lfp0 | Should -Not -BeLike "*${dt0}*" 215 | $lftd.lfp0 | Should -BeLike "*directory1\*" 216 | 217 | $lftd.mode2Ok | Should -Be $false 218 | $lftd.lfp2 | Should -Not -BeLike "*OWNER\user*" 219 | $lftd.lfp2 | Should -Not -BeLike "*GROUP\user*" 220 | $lftd.lfp2 | Should -Not -BeLike "*${dt2}*" 221 | $lftd.lfp2 | Should -BeLike "*file1.txt*" 222 | } 223 | } 224 | } 225 | 226 | -------------------------------------------------------------------------------- /Tests/ModuleShouldLoad.Tests.ps1: -------------------------------------------------------------------------------- 1 | $ROOT = Split-Path -Parent $MyInvocation.MyCommand.Path 2 | 3 | Describe "Importing" { 4 | Context "When module definition is being imported" { 5 | It "Should not have any warnings" { 6 | $warn = $false 7 | $out = (pwsh -NoProfile -Command "Import-Module $ROOT\..\src\PowerColorLS.psd1") 8 | $out | % { $warn = $warn -or ($_ -Match "WARNING") } 9 | $warn | Should be $false 10 | } 11 | } 12 | Context "When module is being imported" { 13 | It "Should not have any warnings" { 14 | $warn = $false 15 | $out = (pwsh -NoProfile -Command "Import-Module $ROOT\..\src\PowerColorLS.psm1") 16 | $out | % { $warn = $warn -or ($_ -Match "WARNING") } 17 | if($true -eq $warn){ 18 | Write-Host -Message $out 19 | } 20 | $warn | Should be $false 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /Tests/ScriptAnalyzerShoulsPassWithNoErrors..Tests.ps1: -------------------------------------------------------------------------------- 1 | Import-Module PSScriptAnalyzer 2 | $ROOT = Split-Path -Parent $MyInvocation.MyCommand.Path 3 | 4 | function Show-ScriptAnalyzerErrors{ 5 | Param($analyzerResults) 6 | if($null -ne $analyzerResults){ 7 | foreach($r in $analyzerResults){ 8 | $errMsg = -join($r.RuleName, " error on line ", $r.Line, ". ", $r.Message) 9 | Write-Warning $errMsg 10 | } 11 | 12 | } 13 | } 14 | 15 | Describe "Analyzing script" { 16 | Context "When script analyzer analyzes the module" { 17 | It "should have no issues" { 18 | $analyzerResults = Invoke-ScriptAnalyzer -Path "$ROOT\..\src\PowerColorLS.psm1" -ExcludeRule PSAvoidUsingWriteHost 19 | Show-ScriptAnalyzerErrors $analyzerResults 20 | $analyzerResults | Should be $null 21 | } 22 | } 23 | Context "When script analyzer analyzes the module manifest" { 24 | It "should have no issues" { 25 | $analyzerResults = Invoke-ScriptAnalyzer -Path "$ROOT\..\src\PowerColorLS.psd1" 26 | Show-ScriptAnalyzerErrors $analyzerResults 27 | $analyzerResults | Should be $null 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /Tests/SharedMocks.ps1: -------------------------------------------------------------------------------- 1 | $glyphs =@{ 2 | 'nf-fa-folder_o' = 'glyph-nf-fa-folder_o' 3 | 'nf-fa-archive' = 'glyph-nf-fa-archive' 4 | 'nf-fa-lock' = 'glyph-nf-fa-lock' 5 | 'nf-mdi-file_hidden' = 'glyph-nf-mdi-file_hidden' 6 | 'nf-fa-gear' = 'glyph-nf-fa-gear' 7 | } 8 | 9 | function Add-MockProperty{ 10 | param( 11 | $obj, 12 | [string]$name, 13 | $value 14 | ) 15 | 16 | $ht = @{ 17 | MemberType = [System.Management.Automation.PSMemberTypes]::NoteProperty 18 | Name = $name 19 | value = $value 20 | Force = $true 21 | } 22 | 23 | $obj | Add-Member @ht -ErrorAction 0 24 | } 25 | 26 | $MockedDirectoryName = "c:\dummy" 27 | 28 | function Get-MockedFileInfo{ 29 | param( 30 | [string]$name, 31 | [datetime]$lastWriteTime 32 | ) 33 | $obj = New-MockObject -Type System.IO.FileInfo 34 | Add-MockProperty -obj $obj -name "PSPath" -value "Microsoft.PowerShell.Core\FileSystem::${MockedDirectoryName}\${name}" 35 | Add-MockProperty -obj $obj -name "Name" -value $name 36 | Add-MockProperty -obj $obj -name "FullName" -value "${MockedDirectoryName}\${name}" 37 | Add-MockProperty -obj $obj -name "LastWriteTime" -value $lastWriteTime 38 | Add-MockProperty -obj $obj -name "Parent" -value @{FullName = $MockedDirectoryName} 39 | Add-MockProperty -obj $obj -name "DirectoryName" -value $null 40 | Add-MockProperty -obj $obj -name "Extension" -value [System.IO.Path]::GetExtension($name) 41 | Add-MockProperty -obj $obj -name "Exists" -value $true 42 | Add-MockProperty -obj $obj -name "Mode" -value "-a---" 43 | return $obj 44 | } 45 | 46 | function Get-MockedDirectoryInfo{ 47 | param( 48 | [string]$name, 49 | [datetime]$lastWriteTime 50 | ) 51 | $obj = New-MockObject -Type System.IO.DirectoryInfo 52 | Add-MockProperty -obj $obj -name "PSPath" -value "Microsoft.PowerShell.Core\FileSystem::${MockedDirectoryName}\${name}" 53 | Add-MockProperty -obj $obj -name "Name" -value $name 54 | Add-MockProperty -obj $obj -name "FullName" -value "${MockedDirectoryName}\${name}" 55 | Add-MockProperty -obj $obj -name "LastWriteTime" -value $lastWriteTime 56 | Add-MockProperty -obj $obj -name "Parent" -value @{FullName = ""} 57 | Add-MockProperty -obj $obj -name "DirectoryName" -value ${MockedDirectoryName} 58 | Add-MockProperty -obj $obj -name "Extension" -value [System.IO.Path]::GetExtension($name) 59 | Add-MockProperty -obj $obj -name "Exists" -value $true 60 | Add-MockProperty -obj $obj -name "Mode" -value "d----" 61 | return $obj 62 | } 63 | 64 | function Get-MockedFileAndDirectoryListing{ 65 | $a = Get-MockedDirectoryInfo -Name "directory1" -LastWriteTime (Get-Date -Date "2020-04-25 02:25:00Z") 66 | $b = Get-MockedDirectoryInfo -Name "directory2" -LastWriteTime (Get-Date -Date "2018-04-25 10:25:00Z") 67 | $c = Get-MockedFileInfo -Name "file1.txt" -LastWriteTime (Get-Date -Date "2020-04-25 11:25:00Z") 68 | $d = Get-MockedFileInfo -Name "file2.txt" -LastWriteTime (Get-Date -Date "2020-04-24 10:25:00Z") 69 | $e = Get-MockedFileInfo -Name "file3.txt" -LastWriteTime (Get-Date -Date "2019-04-25 10:25:00Z") 70 | 71 | $fakeListingOfFilesAndDirectories = @($a, $b, $c, $d, $e) 72 | 73 | return $fakeListingOfFilesAndDirectories 74 | } 75 | 76 | function Get-MockedItem{ 77 | param([string]$Path) 78 | $files = Get-MockedFileAndDirectoryListing 79 | $fileOrDir = $files[0] 80 | if(($null -ne $Path) -and ("" -ne $Path)){ 81 | 82 | } 83 | $fileOrDir.Parent = $null 84 | return $fileOrDir 85 | } 86 | 87 | function Get-MockedOptions{ 88 | param([hashtable]$adjustments) 89 | $options = @{ 90 | showHiddenFiles = $false 91 | dirOnly = $false 92 | fileOnly = $false 93 | sortByModificationTime = $false 94 | filesFirst = $false 95 | dirsFirst = $false 96 | longFormat = $false 97 | showDirectorySize = $false 98 | oneEntryPerLine = $false 99 | showReport = $false 100 | hideIcons = $false 101 | } 102 | if($adjustments -ne $null){ 103 | foreach($adjustmentKey in $adjustments.Keys){ 104 | $options[$adjustmentKey] = $adjustments[$adjustmentKey] 105 | } 106 | } 107 | return $options 108 | } 109 | 110 | function Get-MockedDirectoryAcl{ 111 | param([string]$Path) 112 | $obj = New-Object PSObject -Property @{ 113 | Owner = "OWNER\user" 114 | Group = "GROUP\user" 115 | } 116 | return $obj 117 | } 118 | 119 | function Get-MockedFileAcl{ 120 | param([string]$Path) 121 | $obj = New-Object PSObject -Property @{ 122 | Owner = "OWNER\user" 123 | Group = "GROUP\user" 124 | } 125 | return $obj 126 | } 127 | 128 | 129 | function Get-MockedAclFromPipeline{ 130 | [cmdletbinding()] 131 | param( 132 | [parameter(ValueFromPipelineByPropertyName)] 133 | $Name, 134 | [parameter(ValueFromPipelineByPropertyName)] 135 | $FullName, 136 | [parameter(ValueFromPipelineByPropertyName)] 137 | $DirectoryName 138 | ) 139 | 140 | process { 141 | if($null -eq $DirectoryName){ 142 | return Get-MockedDirectoryAcl 143 | }else{ 144 | return Get-MockedFileAcl 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /Tests/Show-Report.Tests.ps1: -------------------------------------------------------------------------------- 1 | $ROOT = Split-Path -Parent $MyInvocation.MyCommand.Path 2 | 3 | .$ROOT\..\src\Private\Show-Report.ps1 4 | .$ROOT\..\src\Private\FileAndFolderFunctions.ps1 5 | .$ROOT\..\src\Private\Get-Color.ps1 6 | 7 | . $PSScriptRoot/SharedMocks.ps1 8 | 9 | Describe "Show-Report Tests" { 10 | BeforeAll { 11 | Mock -CommandName Get-ChildItem -MockWith { 12 | return Get-MockedFileAndDirectoryListing 13 | } 14 | 15 | Mock -CommandName Write-Host -MockWith { 16 | } 17 | 18 | 19 | } 20 | 21 | $directoryCount = 2 22 | $fileCount = 3 23 | $query = "." 24 | 25 | Context "When showing short format report" { 26 | It "Should write out expected report" { 27 | $options = Get-MockedOptions 28 | [array]$filesAndFolders = (Get-FilesAndFoldersListing -options $options -query $query) 29 | $report = Show-Report -options $options -filesAndFolders $filesAndFolders -query $query 30 | Assert-MockCalled Write-Host -Exactly 2 -Scope It 31 | Assert-MockCalled Write-Host -Exactly 1 -Scope It -ParameterFilter { $Object -like "*Found 5 files and folders matching*" } 32 | Assert-MockCalled Write-Host -Exactly 1 -Scope It -ParameterFilter { $Object -like "*Folders: $directoryCount*" } 33 | Assert-MockCalled Write-Host -Exactly 1 -Scope It -ParameterFilter { $Object -like "*Files: $fileCount*" } 34 | } 35 | } 36 | 37 | Context "When showing long format report" { 38 | It "Should write out expected report" { 39 | 40 | $options = Get-MockedOptions -adjustments @{longFormat = $true;} 41 | 42 | [array]$filesAndFolders = (Get-FilesAndFoldersListing -options $options -query $query) 43 | $report = Show-Report -options $options -filesAndFolders $filesAndFolders -query $query 44 | Assert-MockCalled Write-Host -Exactly 1 -Scope It 45 | Assert-MockCalled Write-Host -Exactly 1 -Scope It -ParameterFilter { $Object -like "*Found 5 files and folders matching*" } 46 | Assert-MockCalled Write-Host -Exactly 1 -Scope It -ParameterFilter { $Object -like "*Folders: $directoryCount*" } 47 | Assert-MockCalled Write-Host -Exactly 1 -Scope It -ParameterFilter { $Object -like "*Files: $fileCount*" } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /media/screens/git.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gardebring/PowerColorLS/5058284d58722df5d25029e8835b62ae84185c38/media/screens/git.png -------------------------------------------------------------------------------- /media/screens/powercolorls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gardebring/PowerColorLS/5058284d58722df5d25029e8835b62ae84185c38/media/screens/powercolorls.png -------------------------------------------------------------------------------- /media/screens/powercolorls_a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gardebring/PowerColorLS/5058284d58722df5d25029e8835b62ae84185c38/media/screens/powercolorls_a.png -------------------------------------------------------------------------------- /media/screens/powercolorls_a_l.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gardebring/PowerColorLS/5058284d58722df5d25029e8835b62ae84185c38/media/screens/powercolorls_a_l.png -------------------------------------------------------------------------------- /media/screens/powercolorls_a_l_show_directory_size.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gardebring/PowerColorLS/5058284d58722df5d25029e8835b62ae84185c38/media/screens/powercolorls_a_l_show_directory_size.png -------------------------------------------------------------------------------- /media/screens/powercolorls_f_d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gardebring/PowerColorLS/5058284d58722df5d25029e8835b62ae84185c38/media/screens/powercolorls_f_d.png -------------------------------------------------------------------------------- /media/screens/powercolorls_obj_l.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gardebring/PowerColorLS/5058284d58722df5d25029e8835b62ae84185c38/media/screens/powercolorls_obj_l.png -------------------------------------------------------------------------------- /src/Init/Import-Dependencies.ps1: -------------------------------------------------------------------------------- 1 | $terminalIconsFolder = [System.IO.Path]::GetDirectoryName((Get-Module Terminal-Icons).path) 2 | $theme = "devblackops" 3 | $glyphs = . $terminalIconsFolder/Data/glyphs.ps1 4 | $iconTheme = Import-PowerShellDataFile "${terminalIconsFolder}/Data/iconThemes/$theme.psd1" 5 | $colorTheme = Import-PowerShellDataFile "${terminalIconsFolder}/Data/colorThemes/$theme.psd1" 6 | 7 | # Dot source private functions 8 | (Get-ChildItem -Path ("$PSScriptRoot/../Private/*.ps1") -Recurse -ErrorAction Stop).ForEach({ 9 | try { 10 | . $_.FullName 11 | } catch { 12 | throw $_ 13 | $PSCmdlet.ThrowTerminatingError("Unable to load [$($import.FullName)]") 14 | } 15 | }) -------------------------------------------------------------------------------- /src/PowerColorLS.psd1: -------------------------------------------------------------------------------- 1 | #Requires -Modules Terminal-Icons 2 | 3 | # 4 | # Module manifest for module 'PowerColorLS' 5 | # 6 | 7 | @{ 8 | 9 | # Script module or binary module file associated with this manifest. 10 | RootModule = 'PowerColorLS.psm1' 11 | 12 | # Version number of this module. 13 | ModuleVersion = '1.0.4' 14 | 15 | # Supported PSEditions 16 | # CompatiblePSEditions = @() 17 | 18 | # ID used to uniquely identify this module 19 | GUID = 'a6467844-8562-42c6-adf1-bab76c2a8a9f' 20 | 21 | # Author of this module 22 | Author = 'Anders Gardebring' 23 | 24 | # Company or vendor of this module 25 | CompanyName = 'Unknown' 26 | 27 | # Copyright statement for this module 28 | Copyright = 'Copyright (c) 2020-2021 Anders Gardebring' 29 | 30 | # Description of the functionality provided by this module 31 | Description = 'List information about files and directories (the current directory by default). 32 | Entries will be sorted alphabetically if no sorting option is specified. 33 | The directories and files will be displayed with an icon and color scheme. 34 | Project site is at https://github.com/gardebring/PowerColorLS 35 | The module has a dependency on the powershell module Terminal-Icons (https://github.com/devblackops/Terminal-Icons/) 36 | being installed and configured first.' 37 | 38 | # Minimum version of the PowerShell engine required by this module 39 | # PowerShellVersion = '' 40 | 41 | # Name of the PowerShell host required by this module 42 | # PowerShellHostName = '' 43 | 44 | # Minimum version of the PowerShell host required by this module 45 | # PowerShellHostVersion = '' 46 | 47 | # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 48 | # DotNetFrameworkVersion = '' 49 | 50 | # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 51 | # CLRVersion = '' 52 | 53 | # Processor architecture (None, X86, Amd64) required by this module 54 | # ProcessorArchitecture = '' 55 | 56 | # Modules that must be imported into the global environment prior to importing this module 57 | # RequiredModules = @() 58 | 59 | # Assemblies that must be loaded prior to importing this module 60 | # RequiredAssemblies = @() 61 | 62 | # Script files (.ps1) that are run in the caller's environment prior to importing this module. 63 | # ScriptsToProcess = @() 64 | 65 | # Type files (.ps1xml) to be loaded when importing this module 66 | # TypesToProcess = @() 67 | 68 | # Format files (.ps1xml) to be loaded when importing this module 69 | # FormatsToProcess = @() 70 | 71 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess 72 | # NestedModules = @() 73 | 74 | # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. 75 | FunctionsToExport = 'PowerColorLS' 76 | 77 | # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. 78 | CmdletsToExport = @() 79 | 80 | # Variables to export from this module 81 | #VariablesToExport = '*' 82 | 83 | # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. 84 | AliasesToExport = @() 85 | 86 | # DSC resources to export from this module 87 | # DscResourcesToExport = @() 88 | 89 | # List of all modules packaged with this module 90 | # ModuleList = @() 91 | 92 | # List of all files packaged with this module 93 | # FileList = @() 94 | 95 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. 96 | PrivateData = @{ 97 | 98 | PSData = @{ 99 | 100 | # Tags applied to this module. These help with module discovery in online galleries. 101 | # Tags = @() 102 | 103 | # A URL to the license for this module. 104 | LicenseUri = 'https://raw.githubusercontent.com/gardebring/PowerColorLS/master/LICENSE.txt' 105 | 106 | # A URL to the main website for this project. 107 | ProjectUri = 'https://github.com/gardebring/PowerColorLS' 108 | 109 | # A URL to an icon representing this module. 110 | # IconUri = '' 111 | 112 | # ReleaseNotes of this module 113 | # ReleaseNotes = '' 114 | 115 | } # End of PSData hashtable 116 | 117 | } # End of PrivateData hashtable 118 | 119 | # HelpInfo URI of this module 120 | # HelpInfoURI = '' 121 | 122 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. 123 | # DefaultCommandPrefix = '' 124 | 125 | } 126 | -------------------------------------------------------------------------------- /src/PowerColorLS.psm1: -------------------------------------------------------------------------------- 1 | #Requires -Modules Terminal-Icons 2 | . $PSScriptRoot/Init/Import-Dependencies.ps1 3 | 4 | function PowerColorLS{ 5 | <# 6 | .Synopsis 7 | Displays a colorized directory and file listing with icons. 8 | 9 | .Description 10 | List information about files and directories (the current directory by default). 11 | Entries will be sorted alphabetically if no sorting option is specified. 12 | The directories and files will be displayed with an icon and color scheme. 13 | The module has a dependency on the powershell module Terminal-Icons (https://github.com/devblackops/Terminal-Icons/) 14 | being installed and configured first. 15 | 16 | Usage: PowerColorLS [OPTION]... [FILE]..." 17 | 18 | options: 19 | -a, --all do not ignore hidden files and files starting with . 20 | -l, --long use a long listing format 21 | -r, --report shows a brief report 22 | -1 list one file per line 23 | -d, --dirs show only directories 24 | -f, --files show only files 25 | -ds, -sds, --sds, --show-directory-size 26 | show directory size (can take a long time) 27 | -hi, --hide-icons hide icons 28 | 29 | sorting options: 30 | 31 | -sd, --sort-dirs, --group-directories-first 32 | sort directories first 33 | -sf, --sort-files, --group-files-first 34 | sort files first 35 | -t, -st, --st 36 | sort by modification time, newest first 37 | 38 | general options: 39 | 40 | -h, --help prints help information 41 | -v, --version show version information 42 | 43 | .Example 44 | # Show help 45 | PowerColorLS -h 46 | 47 | .Example 48 | # Show a lising of all files and directories in the current location sorted by name 49 | PowerColorLS 50 | 51 | .Example 52 | # Show a lising of all files and directories in c:\test sorted by directories first 53 | PowerColorLS -sd c:\test 54 | 55 | .Example 56 | # Show a lising of all files and directories matching *name* in the current location sorted by files first 57 | PowerColorLS -sf *name* 58 | 59 | .Example 60 | # Show a lising of all files and directories in the current location, including hidden files 61 | PowerColorLS --all 62 | 63 | .Example 64 | # Show a lising of all files and directories in the current location, including hidden files, sorted by modification time 65 | PowerColorLS --all -t 66 | 67 | .Example 68 | # Show a lising of all files and directories in the current location in a long format 69 | PowerColorLS --long 70 | 71 | .Example 72 | # Show a lising of all files and directories in the current location in a long format including directory size 73 | PowerColorLS --long --show-directory-size 74 | 75 | #> 76 | 77 | # get our options 78 | $get_optionsResult = Get-OptionsResult -arguments $args 79 | 80 | if($get_optionsResult.continue -eq $false){ 81 | if($null -ne $get_optionsResult.errorMessage){ 82 | # something was wrong with the parameters provided: 83 | $errMsg = (ConvertFrom-RGBColor -RGB ("FF0000")) + $glyphs["nf-fa-warning"] + " " + $get_optionsResult.errorMessage 84 | Write-Host $errMsg 85 | } 86 | return 87 | } 88 | 89 | $query = $get_optionsResult.query 90 | 91 | # load options 92 | $options = $get_optionsResult.options 93 | 94 | # get the items 95 | $filesAndFolders = Get-FilesAndFoldersListing -options $options -query $query 96 | 97 | $fileAndFolderCount = ($filesAndFolders | Measure-Object).Count 98 | 99 | if($fileAndFolderCount -eq 0){ # nothing found 100 | return 101 | } 102 | 103 | # are we in a git directory? If so, get the data we need 104 | $gitInfo = Get-GitInfo -filesAndFolders $filesAndFolders 105 | 106 | # determine the longest items so we can adapt the list to the console window width 107 | $longestItemLength = Get-LongestItemLength -filesAndFolders $filesAndFolders 108 | 109 | $longFormatData = Splat Get-LongFormatData @{ 110 | options = $options 111 | filesAndFolders = $filesAndFolders 112 | longestItemLength = $longestItemLength 113 | } 114 | 115 | $itemSpacerWidth = 4 116 | $lineCharsCounter = 0 117 | 118 | # get how many characters we have available in this console window 119 | $availableCharWith = $host.ui.rawui.buffersize.width 120 | 121 | if($null -eq $availableCharWith){ 122 | $availableCharWith = 150 123 | } 124 | 125 | # start iterating over our items 126 | foreach ($fileSystemInfo in $filesAndFolders) { 127 | 128 | $color = Get-Color -fileSystemInfo $fileSystemInfo -colorTheme $colorTheme 129 | if(-not $options.hideIcons){ 130 | $icon = Get-Icon -fileSystemInfo $fileSystemInfo -iconTheme $iconTheme -glyphs $glyphs 131 | } 132 | 133 | $colorAndIcon = "${color}${icon}" 134 | 135 | $gitColorAndIcon = Get-GitColorAndIcon -gitInfo $gitInfo -fileSystemInfo $fileSystemInfo -glyphs $glyphs -hideIcons $options.hideIcons 136 | $colorAndIcon = "${gitColorAndIcon}${colorAndIcon}" 137 | 138 | 139 | if($options.longFormat){ 140 | $printout = Splat Get-LongFormatPrintout @{ 141 | fileSystemInfo = $fileSystemInfo 142 | options = $options 143 | longFormatData = $longFormatData 144 | colorAndIcon = $colorAndIcon 145 | availableCharWith = $availableCharWith 146 | } 147 | 148 | }else{ 149 | $nameForDisplay = Get-NameForDisplay -fileSystemInfo $fileSystemInfo 150 | if($options.hideIcons){ 151 | $printout = "${icon}${nameForDisplay}" + (" "*($longestItemLength - $nameForDisplay.length + $itemSpacerWidth)) 152 | }else{ 153 | $printout = "${icon} ${nameForDisplay}" + (" "*($longestItemLength - $nameForDisplay.length + $itemSpacerWidth)) 154 | } 155 | $lineCharsCounter += ($printout.length + $gitInfo.lineCharsCounterIncrease) 156 | } 157 | 158 | if ((-not $options.oneEntryPerLine) -and(-not $options.longFormat) -and ( $lineCharsCounter -ge ($availableCharWith)) ) { 159 | Write-Host "" 160 | $lineCharsCounter = ($printout.length + $gitInfo.lineCharsCounterIncrease) 161 | } 162 | 163 | if($options.longFormat){ 164 | Write-Host "${printout}" 165 | }elseif($options.oneEntryPerLine){ 166 | Write-Host "${gitColorAndIcon}${color}${printout}" 167 | }else{ 168 | Write-Host "${gitColorAndIcon}${color}${printout}" -nonewline 169 | } 170 | } 171 | 172 | if($options.showReport){ 173 | Show-Report -options $options -filesAndFolders $filesAndFolders -query $query 174 | } 175 | 176 | if(-not $options.longFormat){ 177 | Write-Host "" 178 | } 179 | } 180 | 181 | Export-ModuleMember -Function PowerColorLs 182 | -------------------------------------------------------------------------------- /src/Private/FileAndFolderFunctions.ps1: -------------------------------------------------------------------------------- 1 | function Get-FriendlySize { 2 | param( 3 | [Parameter(Mandatory = $true)] 4 | [long]$bytes 5 | ) 6 | $sizes='B,KB,MB,GB,TB,PB,EB,ZB' -split ',' 7 | for($i=0; ($bytes -ge 1kb) -and 8 | ($i -lt $sizes.Count); $i++) {$bytes/=1kb} 9 | $N=0; if($i -eq 0) {$N=0} 10 | "{0:N$($N)} {1}" -f $bytes, $sizes[$i] 11 | } 12 | 13 | function Get-IsDirectory{ 14 | Param( 15 | [Parameter(Mandatory = $true)] 16 | $fileSystemInfo 17 | ) 18 | return ($fileSystemInfo.GetType()) -eq [System.IO.DirectoryInfo] 19 | } 20 | 21 | function Get-FileExtension { 22 | Param( 23 | [Parameter(Mandatory = $true)] 24 | [string]$fileName 25 | ) 26 | return [System.IO.Path]::GetExtension($fileName) 27 | } 28 | 29 | function Get-FilesAndFoldersListing{ 30 | param( 31 | [Parameter(Mandatory = $true)] 32 | [hashtable]$options, 33 | 34 | [Parameter(Mandatory = $true)] 35 | [string]$query 36 | ) 37 | 38 | 39 | if($options.showHiddenFiles){ 40 | $filesAndFolders = Get-ChildItem -Path $query -force 41 | }else{ 42 | $filesAndFolders = Get-ChildItem -Path $query 43 | } 44 | 45 | $fileCount = ($filesAndFolders | Measure-Object).Count 46 | 47 | if($fileCount -eq 0){ # nothing found 48 | return 49 | } 50 | 51 | # Remove items that should not be displayed: 52 | $nl = @() 53 | foreach($fileOrFolder in $filesAndFolders){ 54 | $ignoreItem = Get-IgnoreItem -options $options -fileSystemInfo $fileOrFolder 55 | if(-not $ignoreItem){ 56 | $nl += $fileOrFolder 57 | } 58 | } 59 | 60 | if($nl.Length -eq 0){ # nothing left 61 | return 62 | } 63 | 64 | # Sort the list 65 | $filesAndFolders = Get-SortedFilesAndFoldersListing -filesAndFolders $nl -options $options 66 | 67 | return [array]$filesAndFolders 68 | } 69 | 70 | function Get-NameForDisplay{ 71 | param( 72 | [Parameter(Mandatory = $true)] 73 | $fileSystemInfo 74 | ) 75 | 76 | $isDirectory = Get-IsDirectory -fileSystemInfo $fileSystemInfo 77 | $name = $fileSystemInfo.Name 78 | 79 | if($isDirectory){ 80 | if($IsWindows -eq $True){ 81 | return "${name}\" 82 | }else{ 83 | return "/${name}" 84 | } 85 | }else{ 86 | return $name 87 | } 88 | } 89 | 90 | function Get-DirectoryName{ 91 | param( 92 | [Parameter(Mandatory = $true)] 93 | [array]$filesAndFolders 94 | ) 95 | 96 | $f = $filesAndFolders[0] 97 | 98 | $directoryName = $f.Parent.FullName 99 | 100 | if($directoryName.Length -eq 0){ 101 | $directoryName = $f.DirectoryName 102 | } 103 | 104 | # fix for strange bug that occurs intermittently where we no longer get a proper FileSystemInfo object to work with: 105 | if($null -eq $directoryName){ 106 | if($filesAndFolders.Length -gt 1){ 107 | $directoryName = [System.IO.Path]::GetDirectoryName($f.FullName) 108 | } 109 | } 110 | 111 | return $directoryName 112 | } 113 | 114 | function Get-DirectorySize{ 115 | param( 116 | [Parameter(Mandatory = $true)] 117 | [string]$directoryName 118 | ) 119 | $directorySizeInBytes = ((Get-Childitem $directoryName -Recurse -Force -ErrorAction SilentlyContinue | Measure-Object -Sum Length -ErrorAction SilentlyContinue | Select-Object sum).sum) 120 | return Get-FriendlySize -bytes $directorySizeInBytes 121 | } 122 | 123 | function Get-LongestItemLength{ 124 | param( 125 | [Parameter(Mandatory = $true)] 126 | [array]$filesAndFolders 127 | ) 128 | 129 | 130 | #$longestItem = Get-LongestItem -items $filesAndFolders -scriptBlock {return $item.Name} 131 | #$longestItemLength = $longestItem.Name.Length 132 | 133 | # determine the longest items so we can adapt the list to the console window width 134 | #Sometimes it seems powershell go haywire and cannot propertly sort by length, so using his hacky approach to get the longest item instead: 135 | #$longestItem = $filesAndFolders | Select-Object Name, FullName | Sort-Object { "$_".Length } -descending | Select-Object -first 1 136 | $longestItemLength = 0 137 | $longestItem = $null 138 | foreach($fileOrFolder in $filesAndFolders){ 139 | $l = $fileOrFolder.Name.Length 140 | if($l -gt $longestItemLength){ 141 | $longestItemLength = $l 142 | $longestItem = $fileOrFolder 143 | } 144 | } 145 | 146 | $longestItemIsDirectory = Test-Path -path ($longestItem.FullName) -pathtype container 147 | if(($longestItemIsDirectory) -and (-not $options.fileOnly)){ 148 | $longestItemLength += 1 149 | } 150 | 151 | return $longestItemLength 152 | } 153 | 154 | function Get-SortedFilesAndFoldersListing{ 155 | param( 156 | [Parameter(Mandatory = $true)] 157 | [array]$filesAndFolders, 158 | 159 | [Parameter(Mandatory = $true)] 160 | [hashtable]$options 161 | ) 162 | 163 | 164 | if($filesAndFolders.Length -eq 0){ # nothing found 165 | return 166 | } 167 | 168 | if($options.sortByModificationTime){ 169 | return $filesAndFolders | Sort-Object Lastwritetime -descending 170 | }elseif($options.filesFirst){ 171 | return $filesAndFolders | Sort-Object {$_.GetType()} -descending #Attributes 172 | }elseif($options.dirsFirst){ 173 | return $filesAndFolders | Sort-Object {$_.GetType()} #Attributes 174 | }else{ 175 | return $filesAndFolders | Sort-Object Name 176 | } 177 | } 178 | 179 | function Get-IgnoreItem { 180 | param( 181 | [Parameter(Mandatory = $true)] 182 | [hashtable]$options, 183 | 184 | [Parameter(Mandatory = $true)] 185 | [System.IO.FileSystemInfo]$fileSystemInfo 186 | ) 187 | 188 | $isDirectory = Get-IsDirectory -fileSystemInfo $fileSystemInfo 189 | 190 | if((-not $options.showHiddenFiles) -and ($fileSystemInfo.name.StartsWith("."))) { 191 | return $true 192 | } 193 | 194 | if(($options.dirOnly) -and (-not $isDirectory)) { 195 | return $true 196 | } 197 | 198 | if(($options.fileOnly) -and ($isDirectory)) { 199 | return $true 200 | } 201 | 202 | return $false 203 | } -------------------------------------------------------------------------------- /src/Private/Get-Color.ps1: -------------------------------------------------------------------------------- 1 | function ConvertFrom-RGBColor { 2 | [OutputType([System.String])] 3 | param( 4 | [parameter(Mandatory)] 5 | [string]$RGB 6 | ) 7 | 8 | $RGB = $RGB.Replace('#', '') 9 | $r = [convert]::ToInt32($RGB.SubString(0,2), 16) 10 | $g = [convert]::ToInt32($RGB.SubString(2,2), 16) 11 | $b = [convert]::ToInt32($RGB.SubString(4,2), 16) 12 | 13 | $escape = [char]27 14 | return "${escape}[38;2;$r;$g;$b`m" 15 | } 16 | 17 | function Get-Color{ 18 | param( 19 | [Parameter(Mandatory = $true)] 20 | $fileSystemInfo, 21 | 22 | [Parameter(Mandatory = $true)] 23 | [hashtable]$colorTheme 24 | ) 25 | 26 | $colorHex = Get-ColorHex -fileSystemInfo $fileSystemInfo -colorTheme $colorTheme 27 | 28 | return ConvertFrom-RGBColor -RGB ($colorHex) 29 | } 30 | 31 | # Following are internal methods 32 | 33 | function Get-ColorHex{ 34 | param( 35 | [Parameter(Mandatory = $true)] 36 | $fileSystemInfo, 37 | 38 | [Parameter(Mandatory = $true)] 39 | [hashtable]$colorTheme 40 | ) 41 | 42 | $isDirectory = Get-IsDirectory -fileSystemInfo $fileSystemInfo 43 | $name = $fileSystemInfo.name 44 | $fileExt = Get-FileExtension -fileName $fileSystemInfo.FullName 45 | 46 | if($isDirectory){ 47 | $colorHex = Get-FolderColorHex -name $name -colorTheme $colorTheme 48 | }else{ 49 | $colorHex = Get-FileColorHex -name $name -fileExt $fileExt -colorTheme $colorTheme 50 | } 51 | return $colorHex 52 | } 53 | 54 | function Get-FolderColorHex{ 55 | param( 56 | [Parameter(Mandatory = $true)] 57 | [string]$name, 58 | 59 | [Parameter(Mandatory = $true)] 60 | [hashtable]$colorTheme 61 | ) 62 | 63 | $colorHex = $colorTheme.Types.Directories.WellKnown[$name] 64 | if($null -eq $colorHex){ 65 | $colorHex = "EEEE8B" 66 | } 67 | 68 | return $colorHex 69 | } 70 | 71 | function Get-FileColorHex{ 72 | param( 73 | [Parameter(Mandatory = $true)] 74 | [string]$name, 75 | 76 | [Parameter(Mandatory = $true)] 77 | [AllowEmptyString()] 78 | [string]$fileExt, 79 | 80 | [Parameter(Mandatory = $true)] 81 | [hashtable]$colorTheme 82 | ) 83 | 84 | $colorHex = $colorTheme.Types.Files.WellKnown[$name] 85 | if($null -eq $colorHex){ 86 | $colorHex = $colorTheme.Types.Files[$fileExt] 87 | } 88 | if($null -eq $colorHex){ 89 | $colorHex = "EEEEEE" 90 | } 91 | 92 | return $colorHex 93 | } 94 | -------------------------------------------------------------------------------- /src/Private/Get-CommandExist.ps1: -------------------------------------------------------------------------------- 1 | function Get-CommandExist{ 2 | param ( 3 | [Parameter(Mandatory = $true)] 4 | [string]$command 5 | ) 6 | $oldPreference = $ErrorActionPreference 7 | $ErrorActionPreference = "stop" 8 | Try {if(Get-Command $command){return $true}} 9 | Catch {return $false} 10 | Finally {$ErrorActionPreference=$oldPreference} 11 | } -------------------------------------------------------------------------------- /src/Private/Get-Icon.ps1: -------------------------------------------------------------------------------- 1 | function Get-Icon{ 2 | param( 3 | [Parameter(Mandatory = $true)] 4 | $fileSystemInfo, 5 | 6 | [Parameter(Mandatory = $true)] 7 | [hashtable]$iconTheme, 8 | 9 | [Parameter(Mandatory = $true)] 10 | [hashtable]$glyphs 11 | ) 12 | 13 | $iconName = Get-IconName -fileSystemInfo $fileSystemInfo -iconTheme $iconTheme 14 | 15 | return $glyphs[$iconName] 16 | } 17 | 18 | # Following are internal methods 19 | function Get-IconName{ 20 | param( 21 | [Parameter(Mandatory = $true)] 22 | $fileSystemInfo, 23 | 24 | [Parameter(Mandatory = $true)] 25 | [hashtable]$iconTheme 26 | ) 27 | 28 | $isDirectory = Get-IsDirectory -fileSystemInfo $fileSystemInfo 29 | $name = $fileSystemInfo.name 30 | $fileExt = Get-FileExtension -fileName $fileSystemInfo.FullName 31 | 32 | if($isDirectory){ 33 | $iconName = Get-FolderIconName -name $name -iconTheme $iconTheme 34 | }else{ 35 | $iconName = Get-FileIconName -name $name -fileExt $fileExt -iconTheme $iconTheme 36 | } 37 | 38 | $iconName = Get-PatchedIconName -iconName $iconName 39 | 40 | return $iconName 41 | } 42 | 43 | 44 | function Get-PatchedIconName{ 45 | param( 46 | [Parameter(Mandatory = $true)] 47 | [string]$iconName 48 | ) 49 | 50 | switch($iconName){ 51 | "nf-mdi-view_list"{ 52 | return "nf-fa-th_list" 53 | } 54 | "nf-mdi-xml"{ 55 | return "nf-fa-code" 56 | } 57 | default{ 58 | return $iconName 59 | } 60 | } 61 | } 62 | 63 | function Get-FolderIconName{ 64 | param( 65 | [Parameter(Mandatory = $true)] 66 | [string]$name, 67 | 68 | [Parameter(Mandatory = $true)] 69 | [hashtable]$iconTheme 70 | ) 71 | 72 | $iconName = $iconTheme.Types.Directories.WellKnown[$name] 73 | 74 | if($null -eq $iconName){ 75 | $iconName = $iconTheme.Types.Directories[""] 76 | } 77 | 78 | return $iconName 79 | } 80 | 81 | function Get-FileIconName{ 82 | param( 83 | [Parameter(Mandatory = $true)] 84 | [string]$name, 85 | 86 | [Parameter(Mandatory = $true)] 87 | [AllowEmptyString()] 88 | [string]$fileExt, 89 | 90 | [Parameter(Mandatory = $true)] 91 | [hashtable]$iconTheme 92 | ) 93 | 94 | $iconName = $iconTheme.Types.Files.WellKnown[$name] 95 | if($null -eq $iconName){ 96 | $iconName = $iconTheme.Types.Files[$fileExt] 97 | } 98 | if($null -eq $iconName){ 99 | $iconName = $iconTheme.Types.Files[""] 100 | } 101 | 102 | return $iconName 103 | } 104 | 105 | 106 | -------------------------------------------------------------------------------- /src/Private/Get-LongestItem.ps1: -------------------------------------------------------------------------------- 1 | 2 | function Get-LongestItem{ 3 | param( 4 | [Parameter(Mandatory = $true)] 5 | [array]$items, 6 | 7 | [Parameter(Mandatory = $true)] 8 | [scriptblock]$scriptBlock 9 | ) 10 | 11 | $longestItem = "" 12 | foreach($item in $items){ 13 | $itemValue = Invoke-Command -ScriptBlock $scriptBlock 14 | if($itemValue.Length -gt $longestItem.Length){ 15 | $longestItem = $itemValue 16 | } 17 | } 18 | return $longestItem 19 | } -------------------------------------------------------------------------------- /src/Private/Get-OptionsResult.ps1: -------------------------------------------------------------------------------- 1 | function Get-OptionsResult{ 2 | 3 | param( 4 | [array] $arguments 5 | ) 6 | 7 | $options = @{ 8 | oneEntryPerLine = $false 9 | showHiddenFiles = $false 10 | dirOnly = $false 11 | fileOnly = $false 12 | longFormat = $false 13 | dirsFirst = $false 14 | filesFirst = $false 15 | sortByModificationTime = $false 16 | showDirectorySize = $false 17 | showReport = $false 18 | hideIcons = $false 19 | } 20 | 21 | $get_optionsResult = @{ 22 | continue = $true 23 | errorMessage = $null 24 | query = "." 25 | } 26 | 27 | if($arguments){ 28 | foreach($arg in $arguments){ 29 | if($null -ne $arg){ 30 | $a = "$arg" 31 | $isPath = Test-Path -path $a 32 | if($isPath){ 33 | $get_optionsResult.query = $arg 34 | }else{ 35 | $aDashParsed = $a -replace "--", "-" 36 | switch ($aDashParsed) { 37 | {(($aDashParsed -eq "-h") -or ($aDashParsed -eq "-help"))} { 38 | Show-Help 39 | $get_optionsResult.continue = $false 40 | return $get_optionsResult 41 | } 42 | {(($aDashParsed -eq "-v") -or ($aDashParsed -eq "-version"))} { 43 | Show-Version 44 | $get_optionsResult.continue = $false 45 | return $get_optionsResult 46 | } 47 | "-1" { 48 | $options.oneEntryPerLine = $true 49 | } 50 | {(($aDashParsed -eq "-a") -or ($aDashParsed -eq "-all") -or ($aDashParsed -eq "-almost-all"))} { 51 | $options.showHiddenFiles = $true 52 | } 53 | {(($aDashParsed -eq "-d") -or ($aDashParsed -eq "-dirs") -or ($aDashParsed -eq "-directory"))} { 54 | $options.dirOnly = $true 55 | } 56 | {(($aDashParsed -eq "-f") -or ($aDashParsed -eq "-files"))} { 57 | $options.fileOnly = $true 58 | } 59 | {(($aDashParsed -eq "-l") -or ($aDashParsed -eq "-long"))} { 60 | $options.longFormat = $true 61 | } 62 | {(($aDashParsed -eq "-sd") -or ($aDashParsed -eq "-sort-dirs") -or ($aDashParsed -eq "-group-directories-first"))} { 63 | $options.dirsFirst = $true 64 | } 65 | {(($aDashParsed -eq "-sf") -or ($aDashParsed -eq "-sort-files") -or ($aDashParsed -eq "-group-files-first"))} { 66 | $options.filesFirst = $true 67 | } 68 | {(($aDashParsed -eq "-t") -or ($aDashParsed -eq "-st"))} { 69 | $options.sortByModificationTime = $true 70 | } 71 | {(($aDashParsed -eq "-ds") -or ($aDashParsed -eq "-sds") -or ($aDashParsed -eq "-sds") -or ($aDashParsed -eq "-show-directory-size"))} { 72 | $options.showDirectorySize = $true 73 | } 74 | {(($aDashParsed -eq "-r") -or ($aDashParsed -eq "-report"))} { 75 | $options.showReport = $true 76 | } 77 | {(($aDashParsed -eq "-hi") -or ($aDashParsed -eq "-hide-icons"))} { 78 | $options.hideIcons = $true 79 | } 80 | default{ 81 | if($a -like('-*')){ 82 | $get_optionsResult.errorMessage = "invalid option $a" 83 | $get_optionsResult.continue = $false 84 | return $get_optionsResult 85 | 86 | }else{ 87 | $get_optionsResult.errorMessage = "$a is not a valid path" 88 | $get_optionsResult.continue = $false 89 | return $get_optionsResult 90 | } 91 | } 92 | } 93 | } 94 | } 95 | } 96 | } 97 | 98 | $get_optionsResult.options = $options 99 | return $get_optionsResult 100 | } 101 | -------------------------------------------------------------------------------- /src/Private/GitFunctions.ps1: -------------------------------------------------------------------------------- 1 | function Get-GitInfo { 2 | param( 3 | [Parameter(Mandatory = $true)] 4 | [array]$filesAndFolders 5 | ) 6 | 7 | $directoryName = Get-DirectoryName -filesAndFolders $filesAndFolders 8 | 9 | # determine if we should handle this as git directory 10 | $isGitDirectory = Get-ShowAsGitDirectory -directory $directoryName 11 | 12 | $lineCharsCounterIncrease = 0 13 | 14 | if($isGitDirectory){ 15 | $gitStatusItems = Get-GitStatusItemList -directory $directoryName 16 | $lineCharsCounterIncrease = 2 17 | } 18 | 19 | return @{ 20 | isGitDirectory = $isGitDirectory 21 | gitStatusItems = $gitStatusItems 22 | lineCharsCounterIncrease = $lineCharsCounterIncrease 23 | } 24 | } 25 | 26 | 27 | function Get-IsGitDirectory { 28 | param( 29 | [Parameter(Mandatory = $true)] 30 | [string]$directory 31 | ) 32 | 33 | if ((Test-Path "${directory}/.git") -eq $TRUE) { 34 | return $TRUE 35 | } 36 | 37 | # Test within parent dirs 38 | $checkIn = (Get-Item ${directory}).parent 39 | while ($null -ne $checkIn) { 40 | $pathToTest = $checkIn.FullName + '/.git' 41 | if ((Test-Path $pathToTest) -eq $TRUE) { 42 | return $TRUE 43 | } else { 44 | $checkIn = $checkIn.parent 45 | } 46 | } 47 | 48 | return $FALSE 49 | } 50 | 51 | function Get-ShowAsGitDirectory{ 52 | param( 53 | [Parameter(Mandatory = $true)] 54 | [string]$directory 55 | ) 56 | 57 | # check if git directory 58 | $isGitDirectory = Get-IsGitDirectory -directory $directory 59 | 60 | # check if git is installed 61 | $gitIsInstalled = Get-CommandExist -command "git" 62 | 63 | if(-not $gitIsInstalled){ 64 | $isGitDirectory = $false 65 | } 66 | 67 | return $isGitDirectory 68 | } 69 | 70 | function Get-GitStatusItemList{ 71 | param( 72 | [Parameter(Mandatory = $true)] 73 | [string]$directory 74 | ) 75 | 76 | # get the current directory 77 | $currentPath = (Get-Location).Path 78 | 79 | Set-Location -Path $directory 80 | $gitStatus = git status --porcelain=v1 81 | $gitRoot = git rev-parse --show-toplevel 82 | Set-Location -Path $currentPath 83 | 84 | $gitStatusItems = @() 85 | 86 | foreach($gitStatusItem in $gitStatus){ 87 | $gs = $gitStatusItem.Trim().Split(" ") 88 | $l = -join($gitRoot, "/", $gs[1]) 89 | $gitStatusItems += @{ 90 | status = $gs[0] 91 | path = $l 92 | } 93 | } 94 | return $gitStatusItems 95 | } 96 | 97 | function Get-GitColorAndIcon{ 98 | param( 99 | [Parameter(Mandatory = $true)] 100 | [hashtable]$gitInfo, 101 | 102 | [Parameter(Mandatory = $true)] 103 | $fileSystemInfo, 104 | 105 | [Parameter(Mandatory = $true)] 106 | [hashtable]$glyphs, 107 | 108 | [Parameter(Mandatory = $true)] 109 | [bool]$hideIcons 110 | ) 111 | 112 | if(-not $gitInfo.isGitDirectory){ 113 | return "" 114 | } 115 | 116 | if($hideIcons){ 117 | $gitGlyph = "√"; 118 | }else{ 119 | $gitGlyph = $glyphs["nf-fa-check"] 120 | } 121 | 122 | $gitColor = (ConvertFrom-RGBColor -RGB ("00FF00")) 123 | foreach($gitStatusItem in $gitInfo.gitStatusItems){ 124 | $updateGitStatus = $false 125 | $currentItemForGitCompare = $fileSystemInfo.FullName -Replace "\\", "/" 126 | if($currentItemForGitCompare -eq $gitStatusItem.path){ 127 | $updateGitStatus = $true 128 | }elseif($isFolder -and ($gitStatusItem.path.StartsWith($currentItemForGitCompare,'CurrentCultureIgnoreCase'))){ 129 | $updateGitStatus = $true 130 | } 131 | 132 | if($updateGitStatus){ 133 | switch($gitStatusItem.status){ 134 | "??" { 135 | if($hideIcons){ 136 | $gitGlyph = "?"; 137 | }else{ 138 | $gitGlyph = $glyphs["nf-fa-question"] 139 | } 140 | $gitColor = (ConvertFrom-RGBColor -RGB ("FF0000")) 141 | } 142 | default{ 143 | $gitGlyph = $gitStatusItem.status 144 | $gitColor = (ConvertFrom-RGBColor -RGB ("FFFF00")) 145 | } 146 | } 147 | } 148 | } 149 | $gitColorAndIcon = "${gitColor}${gitGlyph} " 150 | return $gitColorAndIcon 151 | } -------------------------------------------------------------------------------- /src/Private/InfoFunctions.ps1: -------------------------------------------------------------------------------- 1 | function Get-Version{ 2 | return $MyInvocation.MyCommand.Module.Version 3 | } 4 | 5 | function Show-Help{ 6 | $v = Get-Version 7 | 8 | $help = " 9 | Help for PowerColorLS version ${v} 10 | Usage: PowerColorLS [OPTION]... [FILE]... 11 | List information about files and directories (the current directory by default). 12 | Entries will be sorted alphabetically if no sorting option is specified. 13 | 14 | -a, --all do not ignore hidden files and files starting with . 15 | -l, --long use a long listing format 16 | -r, --report shows a brief report 17 | -1 list one file per line 18 | -d, --dirs show only directories 19 | -f, --files show only files 20 | -ds, -sds, --sds, --show-directory-size 21 | show directory size (can take a long time) 22 | -hi, --hide-icons hide icons 23 | 24 | sorting options: 25 | 26 | -sd, --sort-dirs, --group-directories-first 27 | sort directories first 28 | -sf, --sort-files, --group-files-first 29 | sort files first 30 | -t, -st, --st 31 | sort by modification time, newest first 32 | 33 | general options: 34 | 35 | -h, --help prints this help 36 | -v, --version show version information 37 | " 38 | Write-Host $help 39 | } 40 | 41 | function Show-Version{ 42 | $v = Get-Version 43 | Write-Host "PowerColorLS version ${v}" 44 | } 45 | -------------------------------------------------------------------------------- /src/Private/LongFormatHelper.ps1: -------------------------------------------------------------------------------- 1 | function Get-LongFormatPrintout{ 2 | param( 3 | [Parameter(Mandatory = $true)] 4 | $fileSystemInfo, 5 | 6 | [Parameter(Mandatory = $true)] 7 | [hashtable]$options, 8 | 9 | [Parameter(Mandatory = $true)] 10 | [hashtable]$longFormatData, 11 | 12 | [Parameter(Mandatory = $true)] 13 | [string]$colorAndIcon, 14 | 15 | [Parameter(Mandatory = $true)] 16 | [int]$availableCharWith 17 | ) 18 | 19 | $isDirectory = Get-IsDirectory -fileSystemInfo $fileSystemInfo 20 | $nameForDisplay = Get-NameForDisplay -fileSystemInfo $fileSystemInfo 21 | $mode = Get-ModeForLongListing -modeInput $fileSystemInfo.Mode -hideIcons $options.hideIcons 22 | $lastWriteTime = ($fileSystemInfo.LastWriteTime).ToString("f") 23 | 24 | try{ 25 | $acl = Get-Acl $fileSystemInfo.FullName 26 | $owner = $acl.Owner 27 | $group = $acl.Group 28 | }catch{ 29 | $owner = "" 30 | $group = "" 31 | } 32 | 33 | if($isDirectory){ 34 | if($options.showDirectorySize){ 35 | $size = Get-DirectorySize -directoryName $fileSystemInfo.FullName 36 | }else{ 37 | $size = "" 38 | } 39 | }else{ 40 | $size = Get-FriendlySize -bytes $fileSystemInfo.Length 41 | } 42 | 43 | $sizeWithSpace = $size.PadRight(8) 44 | 45 | try{ 46 | $ownerSpace = $longFormatData.longestOwnerAclLength - $owner.length 47 | $ownerWithSpace = "${owner}" + (" "*($ownerSpace)) 48 | }catch{ 49 | $ownerWithSpace = "" 50 | } 51 | 52 | try{ 53 | $groupSpace = $longFormatData.longestGroupAclLength - $group.length 54 | $groupWithSpace = "${group}" + (" "*($groupSpace)) 55 | }catch{ 56 | $groupWithSpace = "" 57 | } 58 | 59 | $lwSpace = $longFormatData.longestDateLength - $lastWriteTime.Length 60 | 61 | $lwWithSpace = "${lastWriteTime}" + (" "*($lwSpace)) 62 | 63 | $ownerColor = $longFormatData.ownerColor 64 | $groupColor = $longFormatData.groupColor 65 | $sizeColor = $longFormatData.sizeColor 66 | $lwColor = $longFormatData.lwColor 67 | 68 | if($availableCharWith -gt $longFormatData.fullItemMaxLength){ 69 | $printout = "${mode} ${ownerColor}${ownerWithSpace} ${groupColor}${groupWithSpace} ${sizeColor}${sizeWithSpace} ${lwColor}${lwWithSpace} ${colorAndIcon} ${nameForDisplay}" 70 | }elseif($availableCharWith -gt $longFormatData.noGroupMaxLength){ 71 | $printout = "${mode} ${ownerColor}${ownerWithSpace} ${sizeColor}${sizeWithSpace} ${lwColor}${lwWithSpace} ${colorAndIcon} ${nameForDisplay}" 72 | }elseif($availableCharWith -gt $longFormatData.noGroupOrOwnerMaxLength){ 73 | $printout = "${mode} ${sizeColor}${sizeWithSpace} ${lwColor}${lwWithSpace} ${colorAndIcon} ${nameForDisplay}" 74 | }elseif($availableCharWith -gt $longFormatData.noGroupOrOwnerOrModeMaxLength){ 75 | $printout = "${sizeColor}${sizeWithSpace} ${lwColor}${lwWithSpace} ${colorAndIcon} ${nameForDisplay}" 76 | }else{ 77 | $printout = "${sizeColor}${sizeWithSpace} ${colorAndIcon} ${nameForDisplay}" 78 | } 79 | 80 | return $printout 81 | } 82 | 83 | function Get-LongFormatData{ 84 | param( 85 | [Parameter(Mandatory = $true)] 86 | [hashtable]$options, 87 | 88 | [Parameter(Mandatory = $true)] 89 | [array]$filesAndFolders, 90 | 91 | [Parameter(Mandatory = $true)] 92 | [int]$longestItemLength 93 | ) 94 | 95 | if($options.longFormat){ 96 | Try { 97 | $acls = $filesAndFolders | get-acl 98 | $longestOwnerAcl = Get-LongestItem -items $acls -scriptBlock {return $item.Owner} 99 | $longestGroupAcl = Get-LongestItem -items $acls -scriptBlock {return $item.Group} 100 | } 101 | Catch { 102 | $acls = "" 103 | $longestGroupAcl = "" 104 | } 105 | Finally { 106 | } 107 | 108 | $longestDate = Get-LongestItem -items $filesAndFolders -scriptBlock {return $item.LastWriteTime.ToString("f")} 109 | 110 | $directoryName = Get-DirectoryName -filesAndFolders $filesAndFolders 111 | 112 | # determine if we should handle this as git directory 113 | $isGitDirectory = Get-ShowAsGitDirectory -directory $directoryName 114 | 115 | if($isGitDirectory){ 116 | $gitIncrease = 2 117 | }else{ 118 | $gitIncrease = 0 119 | } 120 | 121 | $longestOwnerAclLength = $longestOwnerAcl.Length 122 | $longestGroupAclLength = $longestGroupAcl.Length 123 | $longestDateLength = $longestDate.Length + 1 124 | 125 | return @{ 126 | longestOwnerAclLength = $longestOwnerAclLength 127 | longestGroupAclLength = $longestGroupAclLength 128 | longestDateLength = $longestDateLength 129 | 130 | # Calculate max lengths of different long outputs so we can determine how much will fit in the console 131 | fullItemMaxLength = (11 + 2 + $longestOwnerAclLength + 2 + $longestGroupAclLength + 2 + 8 + 2 + $longestDateLength + 2 + $longestItemLength + 5 + $gitIncrease) 132 | noGroupMaxLength = (11 + 2 + $longestOwnerAclLength + 2 + 8 + 2 + $longestDateLength + 2 + $longestItemLength + 5 + $gitIncrease) 133 | noGroupOrOwnerMaxLength = (11 + 2 + 8 + 2 + $longestDateLength + 2 + $longestItemLength + 5 + $gitIncrease) 134 | noGroupOrOwnerOrModeMaxLength = (8 + 2 + $longestDateLength + 2 + $longestItemLength + 5 + $gitIncrease) 135 | 136 | ownerColor = (ConvertFrom-RGBColor -RGB ("FDFFBA")) 137 | groupColor = (ConvertFrom-RGBColor -RGB ("D3D865")) 138 | lwColor = (ConvertFrom-RGBColor -RGB ("45B2A1")) 139 | sizeColor = (ConvertFrom-RGBColor -RGB ("FDFFBA")) 140 | } 141 | } 142 | return $null 143 | } 144 | 145 | function Get-Mode-Attribute-Color{ 146 | param( 147 | [Parameter(Mandatory = $true)] 148 | [string]$attribute 149 | ) 150 | 151 | switch($attribute){ 152 | "d" { 153 | return (ConvertFrom-RGBColor -RGB ("EEEE8B")) 154 | } 155 | "a" { 156 | return (ConvertFrom-RGBColor -RGB ("EE82EE")) 157 | } 158 | "r" { 159 | return (ConvertFrom-RGBColor -RGB ("6382FF")) 160 | } 161 | "h" { 162 | return (ConvertFrom-RGBColor -RGB ("BABABA")) 163 | } 164 | "s" { 165 | return (ConvertFrom-RGBColor -RGB ("EDA1A1")) 166 | } 167 | default{ 168 | return (ConvertFrom-RGBColor -RGB ("EEEEEE")) 169 | } 170 | } 171 | } 172 | 173 | function Get-ModeForLongListing{ 174 | param( 175 | [Parameter(Mandatory = $true)] 176 | [string]$modeInput, 177 | 178 | [Parameter(Mandatory = $true)] 179 | [bool]$hideIcons 180 | ) 181 | 182 | $mode = "" 183 | foreach ($m in $modeInput.ToCharArray()) { 184 | $color = Get-Mode-Attribute-Color($m) 185 | if($hideIcons){ 186 | $mode += $color + $m + " " 187 | }else{ 188 | switch($m){ 189 | "d" { 190 | $mode += $color + $glyphs["nf-fa-folder_o"] + " " 191 | } 192 | "a" { 193 | $mode += $color + $glyphs["nf-fa-archive"] + " " 194 | } 195 | "r" { 196 | $mode += $color + $glyphs["nf-fa-lock"] + " " 197 | } 198 | "h" { 199 | $mode += $color + $glyphs["nf-mdi-file_hidden"] + " " 200 | } 201 | "s" { 202 | $mode += $color + $glyphs["nf-fa-gear"] + " " 203 | } 204 | default{ 205 | $mode += $color + $m + " " 206 | } 207 | } 208 | } 209 | } 210 | return $mode 211 | } -------------------------------------------------------------------------------- /src/Private/Show-Report.ps1: -------------------------------------------------------------------------------- 1 | function Show-Report{ 2 | param( 3 | [Parameter(Mandatory = $true)] 4 | [hashtable]$options, 5 | 6 | [Parameter(Mandatory = $true)] 7 | [array]$filesAndFolders, 8 | 9 | [Parameter(Mandatory = $true)] 10 | [string]$query 11 | ) 12 | 13 | $directoryCount = ($filesAndFolders | Where-Object {$_.GetType() -eq [System.IO.DirectoryInfo]}).Length 14 | $fileCount = ($filesAndFolders | Where-Object {$_.GetType() -eq [System.IO.FileInfo]}).Length 15 | $itemsLength = $filesAndFolders.Length 16 | 17 | $queryColor = (ConvertFrom-RGBColor -RGB ("00AAFF")) 18 | $baseColor = (ConvertFrom-RGBColor -RGB ("FFFFFF")) 19 | 20 | $report = " 21 | ${baseColor}Found ${itemsLength} files and folders matching ${queryColor}$query${baseColor} 22 | Folders: $directoryCount 23 | Files: $fileCount 24 | " 25 | 26 | if(-not $options.longFormat){ 27 | Write-Host "" 28 | } 29 | 30 | Write-Host $report 31 | } -------------------------------------------------------------------------------- /src/Private/Splat.ps1: -------------------------------------------------------------------------------- 1 | function Splat { 2 | param( 3 | [string] 4 | $FunctionName, 5 | 6 | [hashtable] 7 | $Params 8 | ) 9 | 10 | & $FunctionName @Params 11 | } --------------------------------------------------------------------------------