functions/ShowTree.ps1

function Show-Tree {

    [CmdletBinding(DefaultParameterSetName = "Path")]
    param(
        [Parameter(Position = 0, 
            ParameterSetName = "Path", 
            ValueFromPipeline = $true, 
            ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNullOrEmpty()]
        [string[]]$Path,
       
        [Alias("PSPath")]
        [Parameter(Position = 0, 
            ParameterSetName = "LiteralPath", 
            ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNullOrEmpty()]
        [string[]]$LiteralPath,
           
        [Parameter(Position = 1)]
        [ValidateRange(0, 2147483647)]
        [int]$Depth = [int]::MaxValue, 
       
        [Parameter()]
        [ValidateRange(1, 100)]
        [int]$IndentSize = 3, 
       
        [Parameter()]
        [switch]$ShowItem, 
       
        [Parameter()]
        [switch]$ShowProperty
    )

    Begin {
        Write-Verbose "Starting $($myinvocation.MyCommand)"
        if (!$Path -and $psCmdlet.ParameterSetName -eq "Path") {
            $Path = Get-Location
        }        
       
        function GetIndentString {
            [CmdletBinding()]
            Param([bool[]]$IsLast)

            Write-Verbose "Starting $($myinvocation.MyCommand)"
            $numPadChars = 1
            $str = ''
            for ($i = 0; $i -lt $IsLast.Count - 1; $i++) {
                $sepChar = if ($IsLast[$i]) {' '} else {'|'}
                $str += "$sepChar"
                $str += " " * ($IndentSize - 1)
            }

            $teeChar = if ($IsLast[-1]) {'\'} else {'+'}
            $str += "$teeChar"
            $str += "-" * ($IndentSize - 1)
            $str

            Write-Verbose "Ending $($myinvocation.MyCommand)"
        } 

        function ShowProperty() {
            [cmdletbinding()]
            Param(
                [string]$Name, 
                [string]$Value, 
                [bool[]]$IsLast
            )
            Write-Verbose "Starting $($myinvocation.MyCommand)"
            $indentStr = GetIndentString $IsLast
            $propStr = "${indentStr}Property: $Name = "
            $availableWidth = $host.UI.RawUI.BufferSize.Width - $propStr.Length - 1
            if ($Value.Length -gt $availableWidth) {
                $ellipsis = '...'
                $val = $Value.Substring(0, $availableWidth - $ellipsis.Length) + $ellipsis
            }
            else {
                $val = $Value
            }
            $propStr += $val
            $propStr
            Write-Verbose "Ending $($myinvocation.MyCommand)"
        }
        function ShowItem {
            [CmdletBinding()]
            Param(
            [string]$Path, 
            [string]$Name, 
            [bool[]]$IsLast, 
            [bool]$HasChildItems = $false
            )
            Write-Verbose "Starting $($myinvocation.MyCommand)"
            if ($IsLast.Count -eq 0) {
                "$(Resolve-Path $Path)"
            }
            else {
                $indentStr = GetIndentString $IsLast
                "$indentStr$Name" 
            }
           
            if ($ShowProperty) {
                $IsLast += @($false)

                $excludedProviderNoteProps = 'PSChildName', 'PSDrive', 'PSParentPath', 'PSPath', 'PSProvider'
                $props = @(Get-ItemProperty $Path -ea 0)
                if ($props[0] -is [pscustomobject]) {
                    $props = @($props[0].psobject.properties | Where-object {$excludedProviderNoteProps -notcontains $_.Name})
                }
               
                for ($i = 0; $i -lt $props.Count; $i++) {
                    $prop = $props[$i]
                    $IsLast[-1] = ($i -eq $props.count - 1) -and !$HasChildItems
                    ShowProperty $prop.Name $prop.Value $IsLast
                }
            }
            Write-Verbose "Ending $($myinvocation.MyCommand)"
        }

        function ShowContainer {  
            [CmdletBinding()]
            Param (
                [string]$Path, 
                [string]$Name = $(Split-Path $Path -Leaf), 
                [bool[]]$IsLast = @() 
            )

            Write-Verbose "Starting $($myinvocation.MyCommand) on $Path"
            if ($IsLast.Count -gt $Depth) { return }
           
            $childItems = @()  
            if ($IsLast.Count -lt $Depth) {
                try {
                    $rpath = Resolve-Path $Path -ErrorAction stop
                }
                catch {
                    Throw "Failed to resolve $path. This PSProvider and path may be incompatible with this command."
                    #bail out
                    return
                }
                $childItems = @(Get-ChildItem $rpath -ErrorAction $ErrorActionPreference | 
                        Where-object {$ShowItem -or $_.PSIsContainer})
            }
            $hasChildItems = $childItems.Count -gt 0

            # Show the current container
            ShowItem $Path $Name $IsLast $hasChildItems    

            # Process the children of this container
            $IsLast += @($false)
            for ($i = 0; $i -lt $childItems.count; $i++) {
                $childItem = $childItems[$i]
                $IsLast[-1] = ($i -eq $childItems.count - 1)
                if ($childItem.PSIsContainer) {
                    ShowContainer $childItem.PSPath $childItem.PSChildName $IsLast
                }
                elseif ($ShowItem) {
                    $unresolvedPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($childItem.PSPath)
                    $name = Split-Path $unresolvedPath -Leaf
                    ShowItem $childItem.PSPath $name $IsLast    
                }
            }
            Write-Verbose "Ending $($myinvocation.MyCommand)"
        }
    } #begin

    Process {
        if ($psCmdlet.ParameterSetName -eq "Path") {
            # In the -Path (non-literal) resolve path in case it is wildcarded.
            $resolvedPaths = @($Path | Resolve-Path | Foreach-object {$_.Path})
        }
        else {
            # Must be -LiteralPath
            $resolvedPaths = @($LiteralPath)
        }

        foreach ($rpath in $resolvedPaths) {
            Write-Verbose "Processing $rpath"
            ShowContainer $rpath
        }  
    } #process
    end {
        Write-Verbose "Ending $($myinvocation.MyCommand)"
    }
}