Commands/FormattingExtended/Out-Alternate.ps1

function Out-Alternate
{
    <#
    .SYNOPSIS
        Outputs alternate views
    .DESCRIPTION
        Outputs alternate views of an object or typename.
        
        While it is possible to display multiple views of an object in PowerShell, it's not as straightforward to see what those views are.
        
        Out-Alternate solves this problem, and is embeddable within a format file.
    .EXAMPLE
        Out-Alternate -TypeName "System.Diagnostics.Process"
    #>

    [Management.Automation.Cmdlet("Format", "Object")]
    param(
    # An input object. If this is provided, it will infer the typenames
    [Parameter(ValueFromPipeline)]
    [PSObject]
    $InputObject,
    
    # The typename of the alternate.
    # If this is not provided, it can be inferred from the `-InputObject`.
    [Alias('TypeName')]
    [string[]]
    $PSTypeName,

    # The name of the current view.
    # If this is provided, it will not be displayed as an alternate.
    [string]
    $CurrentView,

    # A prefix to each view.
    [string]
    $Prefix = "",

    # A suffix to each view.
    [string]
    $Suffix,

    # If set, will not padd the space between the name of the format control and the -View parameter
    [switch]
    $NoPadding,

    # The name of one or more modules.
    # If provided, will provide the -PS1XMLPath of each module's .ExportedFormatFiles
    [string[]]
    $ModuleName,

    # The path to one or more .ps1xml files.
    # If these are provided (or inferred thru -ModuleName), will look for alternates in PS1XML.
    [string[]]
    $PS1XMLPath
    )
    
    begin {
        if (-not $script:AlternateViewCache) {
            $script:AlternateViewCache = @{}
        } 
    }

    process {
        # If no typename was provided
        if (-not $PSTypeName) {
            # and the input object has one
            if ($InputObject.pstypenames) {
                # use the input's PSTypeNames
                $PSTypeName = $InputObject.pstypenames
            }
        }

        # If we have no PSTypename, return.
        if (-not $PSTypeName) { return }

        if (-not $script:AlternateViewCache["$PSTypeName"]) {
            if ($ModuleName -and -not $PS1XMLPath) {                
                $PS1XMLPath = (Get-Module $ModuleName).ExportedFormatFiles
            }
                
            $script:AlternateViewCache["$PSTypeName"] =  # Get the views and force them into an array
                @(if ($PS1XMLPath) {
                    foreach ($ps1xml in $PS1XMLPath) {
                        if (-not $ps1xml) { continue }
                        Select-Xml -Path $ps1xml -XPath //TypeName |
                            & { process {
                                if ($_.Node.InnerText -and 
                                    $_.Node.InnerText.Trim() -notin $PSTypeName
                                ) {
                                    return
                                }
                                $_.Node.ParentNode.ParentNode
                            } }                    
                    }
                } else {
                    foreach ($typeName in $PSTypeName) {
                        foreach ($view in (Get-FormatData -TypeName $TypeName).FormatViewDefinition) {
                            $view
                        }
                    }
                })
        }

        $views = $script:AlternateViewCache["$PSTypeName"]

        # Now we walk over each view
        @(foreach ($view in $views) {
            # If we provided a -CurrentView, and this is it, skip.
            if ($CurrentView -and $view.Name -eq $CurrentView) { continue }
            # Determine the format type
            $formatType = if ($view.Control) {
                # by getting the control and removing "Control" from the typename (if using Get-FormatData).
                $view.Control.GetType().Name -replace 'Control$'
            } else {
                foreach ($potentialType in 'Table', 'List', 'Wide', 'Custom') {
                    if ($view."${potentialType}Control") {
                        $potentialType;break
                    }
                }
            }

            if (-not $formatType) { continue }
            

            # By default, we pad space (for aesthetic reasons).
            if (-not $NoPadding) { # If `-NoPadding` was passed, we won't pad space.
                # Otherwise, always pad to 6.
                # (the length of 'Custom', the longest of the potential format types)
                $formatType = $formatType.PadRight(6, ' ')
            }
            # Now output the name of the format command and it's view.
            "Format-$formatType -View '$($view.Name -replace "'", "''")'"
        }) -join [Environment]::NewLine
    }    
}