Windows – How to hide file extensions in PowerShell tab completion

command linepowershellwindows

How do I change the default Powershell CLI behavior to ignore file extensions for filetypes that live on my $Env:Path environment variable?

If I just type python, it works correctly and drops me into the Python interpreter as I expect since the extension is part of the environment variable.

The problem is that when I type pyth and tab-complete in PowerShell, it always appends the .exe. I just want it to finish the first part of the command without the extension.

Is this possible? Maybe a script?

Best Answer

You can override the standard tab completion function with your own. In the newest version of PowerShell, that function is TabExpansion2. This modification of it appears to do what you want:

Function TabExpansion2 {
    [CmdletBinding(DefaultParameterSetName = 'ScriptInputSet')]
    Param(
        [Parameter(ParameterSetName = 'ScriptInputSet', Mandatory = $true, Position = 0)]
        [string] $inputScript,

        [Parameter(ParameterSetName = 'ScriptInputSet', Mandatory = $true, Position = 1)]
        [int] $cursorColumn,

        [Parameter(ParameterSetName = 'AstInputSet', Mandatory = $true, Position = 0)]
        [System.Management.Automation.Language.Ast] $ast,

        [Parameter(ParameterSetName = 'AstInputSet', Mandatory = $true, Position = 1)]
        [System.Management.Automation.Language.Token[]] $tokens,

        [Parameter(ParameterSetName = 'AstInputSet', Mandatory = $true, Position = 2)]
        [System.Management.Automation.Language.IScriptPosition] $positionOfCursor,

        [Parameter(ParameterSetName = 'ScriptInputSet', Position = 2)]
        [Parameter(ParameterSetName = 'AstInputSet', Position = 3)]
        [Hashtable] $options = $null
    )

    End
    {
        $source = $null
        if ($psCmdlet.ParameterSetName -eq 'ScriptInputSet')
        {
            $source = [System.Management.Automation.CommandCompletion]::CompleteInput(
                <#inputScript#>  $inputScript,
                <#cursorColumn#> $cursorColumn,
                <#options#>      $options)
        }
        else
        {
            $source = [System.Management.Automation.CommandCompletion]::CompleteInput(
                <#ast#>              $ast,
                <#tokens#>           $tokens,
                <#positionOfCursor#> $positionOfCursor,
                <#options#>          $options)
        }
        $field = [System.Management.Automation.CompletionResult].GetField('completionText', 'Instance, NonPublic')
        $source.CompletionMatches | % {
            If ($_.ResultType -eq 'Command' -and [io.file]::Exists($_.ToolTip)) {
                $field.SetValue($_, [io.path]::GetFileNameWithoutExtension($_.CompletionText))
            }
        }
        Return $source
    }    
}

I added the lines after the one that starts with $field; it goes through the default tab completion options and lops the extension off ones that appear to be from your PATH. I got the original source with this command:

(Get-Command 'TabExpansion2').ScriptBlock

If you put the new function in a .ps1 file and dot-execute that script (e.g. . .\tabnoext.ps1), it will become the tab completion handler for the current session. To load it every time you open a PowerShell window, just paste all that code into a PowerShell profile script.

If you're on an old version of PowerShell, you need to override the TabExpansion function, which just returns an array of strings.

Related Question