ConvertTo-ShortenedPath.psm1

function ConvertTo-ShortenedPath {
    <#
        .SYNOPSIS
            Path Shortener
        .DESCRIPTION
            Function to convert paths to a shortend form.
            Usefull in prompts to keep the total path length down.
        .EXAMPLE
            ConvertTo-ShortenedPath
            Will shorten the current path, using default parameter values.
        .EXAMPLE
            $path | ConvertTo-ShortenedPath -ReplaceHome:$false
            Will shorten the path in the $path variable. The home path will not be replaced.
        .EXAMPLE
            ConvertTo-ShortenedPath -Path $pwd -MaxSegmentLength 1
            Will shorten the current path, while truncating all but the first and last
            path segments to just one character.
        .EXAMPLE
            ConvertTo-ShortenedPath -Path $pwd -MaxSegmentLength 1 -TruncateFirstSegment
            Will shorten the current path, while truncating all but the last
            path segment to just one character.
        .INPUTS
            System.String
        .OUTPUTS
            SYstem.String
        .LINK
            https://communary.net/
        .NOTES
            Rewrite of my old function 'Invoke-PathShortener'
 
            Author: Øyvind Kallstad
            Date: 13.05.2017
            Version: 1.0
    #>

    [CmdletBinding()]
    param (
        # The path to shorten. Defaults to the current location.
        [Parameter(Position = 0, ValueFromPipeline = $true)]
        [string] $Path = (Get-Location),

        # Number of segments to keep before truncating. Default value is 2.
        [Parameter()]
        [Alias('Before')]
        [ValidateRange(0, [int32]::MaxValue)]
        [int] $KeepBefore = 2,

        # Number of segments to keep after truncating. Default value is 1.
        [Parameter()]
        [Alias('After')]
        [ValidateRange(1, [int32]::MaxValue)]
        [int] $KeepAfter = 1,

        # Maximum length of each segment. If set, will truncate each path segment to the desired length.
        # Default value is null, which means no truncating of the segments will take place.
        [Parameter()]
        [Alias('SegmentLength')]
        [nullable[int]] $MaxSegmentLength = $null,

        # Choose whether to truncate the first path segment or not. Default value is false.
        [Parameter()]
        [Alias('First')]
        [switch] $TruncateFirstSegment = $false,

        # Choose whether to truncate the last path segment or not. Default value is false.
        [Parameter()]
        [Alias('Last')]
        [switch] $TruncateLastSegment = $false,

        # Character(s) to use in place of the truncated path segments.
        # Default value is '[char]8230' (horizontal ellipsis).
        [Parameter()]
        [string] $TruncateCharacter = [char]8230,

        # Choose whether to replace the home path. If set will replace the home path
        # with the value of the HomeReplacementCharacter parameter.
        # Default value is true.
        [Parameter()]
        [switch] $ReplaceHome = $true,

        # Character(s) to use as replacement for the home path. Default value is '~'.
        [Parameter()]
        [string] $HomeCharacter = '~',

        # Path separator character.
        # Defaults to DirectorySeparatorChar from the System.IO.Path class.
        [Parameter()]
        [string] $Separator = [System.IO.Path]::DirectorySeparatorChar,

        # Custom separator in the shortened path.
        # If set, the path separator character will be replaced with this in the output string.
        [Parameter()]
        [nullable[char]] $OutputSeparator = $null
    )

    # Support environment variables that override default parameter values
    if (-not($PSBoundParameters.Keys -icontains 'KeepBefore')) {
        if (Test-Path -Path 'env:PathShortenerKeepBefore') {
            $KeepBefore = $env:PathShortenerKeepBefore
        }
    }
    if (-not($PSBoundParameters.Keys -icontains 'KeepAfter')) {
        if (Test-Path -Path 'env:PathShortenerKeepAfter') {
            $KeepAfter = $env:PathShortenerKeepAfter
        }
    }
    if (-not($PSBoundParameters.Keys -icontains 'MaxSegmentLength')) {
        if (Test-Path -Path 'env:PathShortenerMaxSegmentLength') {
            $MaxSegmentLength = $env:PathShortenerMaxSegmentLength
        }
    }
    if (-not($PSBoundParameters.Keys -icontains 'TruncateFirstSegment')) {
        if (Test-Path -Path 'env:PathShortenerTruncateFirstSegment') {
            $TruncateFirstSegment = $env:PathShortenerTruncateFirstSegment
        }
    }
    if (-not($PSBoundParameters.Keys -icontains 'TruncateLastSegment')) {
        if (Test-Path -Path 'env:PathShortenerTruncateLastSegment') {
            $TruncateLastSegment = $env:PathShortenerTruncateLastSegment
        }
    }
    if (-not($PSBoundParameters.Keys -icontains 'TruncateCharacter')) {
        if (Test-Path -Path 'env:PathShortenerTruncateCharacter') {
            $TruncateCharacter = $env:PathShortenerTruncateCharacter
        }
    }
    if (-not($PSBoundParameters.Keys -icontains 'ReplaceHome')) {
        if (Test-Path -Path 'env:PathShortenerReplaceHome') {
            $ReplaceHome = $env:PathShortenerReplaceHome
        }
    }
    if (-not($PSBoundParameters.Keys -icontains 'HomeCharacter')) {
        if (Test-Path -Path 'env:PathShortenerHomeCharacter') {
            $HomeCharacter = $env:PathShortenerHomeCharacter
        }
    }
    if (-not($PSBoundParameters.Keys -icontains 'Separator')) {
        if (Test-Path -Path 'env:PathShortenerSeparator') {
            $Separator = $env:PathShortenerSeparator
        }
    }
    if (-not($PSBoundParameters.Keys -icontains 'OutputSeparator')) {
        if (Test-Path -Path 'env:PathShortenerOutputSeparator') {
            $OutputSeparator = $env:PathShortenerOutputSeparator
        }
    }

    $outPath = New-Object -TypeName System.Text.StringBuilder

    # Remove 'Microsoft.PowerShell.Core\FileSystem::' from UNC paths
    $Path = $Path -replace('^.*::','')

    # Replace home path
    if ($ReplaceHome) {
        if ($Path.ToLower().StartsWith($HOME.ToLower())) {
            $Path = $Path -ireplace(([regex]::Escape($HOME)),$HomeCharacter)
            $KeepBefore++
        }
    }

    # Split path into segments
    $pathSegments = $Path.Split($Separator.ToString(),[System.StringSplitOptions]::RemoveEmptyEntries)

    # Replace separator character
    if ($OutputSeparator) {
        $Separator = $OutputSeparator
    }

    # Truncate segments
    if ($MaxSegmentLength) {
        if ($MaxSegmentLength -ge 1) {
            $tmpArray = New-Object -TypeName System.Collections.ArrayList
            for ($i = 0; $i -lt $pathSegments.Count; $i++) {

                # Make sure that the MaxSegmentLength is not greater than the actual segment length
                if ($pathSegments[$i].Length -lt $MaxSegmentLength) {
                    $_maxSegmentLength = $pathSegments[$i].Length
                }
                else {
                    $_maxSegmentLength = $MaxSegmentLength
                }

                # first segment
                if ($i -eq 0) {
                    if ($TruncateFirstSegment) {
                        [void]$tmpArray.Add(($pathSegments[$i].SubString(0,$_maxSegmentLength)))
                    }
                    else {
                        [void]$tmpArray.Add(($pathSegments[$i]))
                    }
                }
                # last segment
                elseif ($i -eq ($pathSegments.Count - 1)) {
                    if ($TruncateLastSegment) {
                        [void]$tmpArray.Add(($pathSegments[$i].SubString(0,$_maxSegmentLength)))
                    }
                    else {
                        [void]$tmpArray.Add(($pathSegments[$i]))
                    }
                }
                # the rest of the segments
                else {
                    [void]$tmpArray.Add(($pathSegments[$i].SubString(0,$_maxSegmentLength)))
                }
            }
            $pathSegments = $tmpArray
        }
    }

    # Check if path needs shortening
    if ($pathSegments.Count -gt ($KeepBefore + $KeepAfter)) {
        # Add segments before truncate character(s) to the output string
        for ($i = 0; $i -lt $KeepBefore; $i++) {
            [void]$outPath.Append($pathSegments[$i] + $Separator)
        }

        # Add the truncate character(s) to the output string
        [void]$outPath.Append("$($TruncateCharacter)$($Separator)")

        # Add segments after truncate character(s) to the output string
        for ($i = ($pathSegments.Count - $KeepAfter); $i -lt $pathSegments.Count; $i++) {
            # Don't add the separator for the last segment
            if ($i -eq ($pathSegments.Count - 1)) {
                [void]$outPath.Append($pathSegments[$i])
            }
            else {
                [void]$outPath.Append($pathSegments[$i] + $Separator)
            }
        }
    }
    # Not enought segments to shorten the path
    else {
        [void]$outPath.Append(($pathSegments -join $Separator))
    }

    # \\ is lost for UNC paths, so re-insert this
    if ($Path.StartsWith('\\')) {
        [void]$outPath.Insert(0,'\\')
    }

    Write-Output $outPath.ToString()
}

Export-ModuleMember -Function ConvertTo-ShortenedPath