For example, when I type wg
and then press tab
key, I get wget.exe
, I would instead like to get just wget
. while I am happy with most things this one feature really annoys me.
Even worse, when I try to modify a previously run line, or correcting something, I am finding myself constantly deleting letters that should not have been there...For example, editing the following first line to become the second line:
wget.exe -O - https://www.voidtools.com/forum/viewtopic.php?t=10860) |html2text.exe
wget -O - https://www.voidtools.com/forum/viewtopic.php?t=10860) |html2text
Okay, the above is not the best example but its what I can think of on the spot. Also this is causing me to declare unnecessary aliases all over the place just to escape this ".exe":
...
Set-Alias -Name Edge -value "C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe"
Set-Alias -Name Calibre -value "C:\Program Files\Calibre2\calibre.exe"
Set-Alias -Name CalibreConvert -value "C:\Program Files\Calibre2\ebook-convert.exe"
Set-Alias -Name Simplewall -value "C:\Program Files\simplewall\simplewall.exe"
Set-Alias -Name TeraCopy -value "C:\Program Files\TeraCopy\TeraCopy.exe"
...
Set-Alias -Name MSPaint -value "C:\Program Files\WindowsApps\Microsoft.Paint_11.2110.0.0_x64__8wekyb3d8bbwe\PaintApp\mspaint.exe"
Set-Alias -Name Diagrams -value "https://app.diagrams.net/"
Set-Alias -Name ZBrush -value "C:\Program Files\Pixologic\ZBrush 2021\ZBrush.exe"
Set-Alias -Name Synology -value "C:\Program Files (x86)\Synology\SynologyDrive\bin\launcher.exe"
Set-Alias -Name WireGuard -value "C:\Program Files\WireGuard\wireguard.exe"
...
Of course PowerShell accepts wget
, some of you may say this is dangerous, I don't really care, its my shell and system, I know how it works.
I believe on Linux systems, you get just the name auto completed, that is exactly how I want it...
Has anyone else ever solved this issue??
Of course, I should like this limited to just executables and not effect file names.
I am on Pwsh 7.4, windows 11
Edit: wget is actually a external command, it is a widnows release of the old linux tool, wget.
This isn't related to PSReadLine, it's related to TabExpansion2.
(Get-Command TabExpansion2).Definition
The reason you get wget.exe
from wget
is due to the CommandCompletion
Class used in the function to provide completion results when you press TAB, i.e.:
PS ..\pwsh> [System.Management.Automation.CommandCompletion]::CompleteInput('wing', 4, $null).CompletionMatches
# CompletionText ListItemText ResultType ToolTip
# -------------- ------------ ---------- -------
# winget.exe winget.exe Command C:\path\to\my\winget.exe
Now, I'm totally opposed to the idea of removing that annoying .exe
at the end of external commands but since you're asking and as you've said, it's your shell and system, the solution to your problem is to either create new instances of CompletionResult
or to update the CompletionText
Property for each completion match, the latter option requires reflection. If you agree to that, then you can add this version of TabExpansion2
to your $Profile
to override its default behavior.
function TabExpansion2 {
[CmdletBinding(DefaultParameterSetName = 'ScriptInputSet')]
[OutputType([System.Management.Automation.CommandCompletion])]
Param(
[Parameter(ParameterSetName = 'ScriptInputSet', Mandatory = $true, Position = 0)]
[AllowEmptyString()]
[string] $inputScript,
[Parameter(ParameterSetName = 'ScriptInputSet', Position = 1)]
[int] $cursorColumn = $inputScript.Length,
[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 {
if ($psCmdlet.ParameterSetName -eq 'ScriptInputSet') {
$toComplete = [System.Management.Automation.CommandCompletion]::CompleteInput(
<#inputScript#> $inputScript,
<#cursorColumn#> $cursorColumn,
<#options#> $options)
}
else {
$toComplete = [System.Management.Automation.CommandCompletion]::CompleteInput(
<#ast#> $ast,
<#tokens#> $tokens,
<#positionOfCursor#> $positionOfCursor,
<#options#> $options)
}
if (-not $_field) {
if ($IsCoreCLR) {
$fieldName = '_completionText'
}
else {
$fieldName = 'completionText'
}
$script:_field = [System.Management.Automation.CompletionResult].GetField(
$fieldName,
[System.Reflection.BindingFlags] 'Instance, NonPublic')
}
foreach ($item in $toComplete.CompletionMatches) {
if ($item.CompletionText.EndsWith('.exe', [StringComparison]::InvariantCultureIgnoreCase)) {
$_field.SetValue(
$item,
[System.IO.Path]::GetFileNameWithoutExtension($item.CompletionText))
}
}
$toComplete
}
}