Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tab completion does not work since PowerShell 7.4 #3364

Closed
5 tasks done
yan12125 opened this issue Nov 30, 2023 · 12 comments · Fixed by #3459
Closed
5 tasks done

Tab completion does not work since PowerShell 7.4 #3364

yan12125 opened this issue Nov 30, 2023 · 12 comments · Fixed by #3459
Assignees
Milestone

Comments

@yan12125
Copy link

yan12125 commented Nov 30, 2023

Checklist

  • I have verified this is the correct repository for opening this issue.
  • I have verified no other issues exist related to my problem.
  • I have verified this is not an issue for a specific package.
  • I have verified this issue is not security related.
  • I confirm I am using official, and not unofficial, or modified, Chocolatey products.

What You Are Seeing?

Hitting tab after typing choco only shows filenames

What is Expected?

Choco commands like install, upgrade, etc. should be shown

How Did You Get This To Happen?

  1. Make sure PowerShell profile contains the following snippet
$ChocolateyProfile = "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"
if (Test-Path($ChocolateyProfile)) {
  Import-Module "$ChocolateyProfile"
}
  1. Upgrade PowerShell to 7.4.0
  2. Type choco and hit tab

System Details

  • Operating System: 10.0.19045.0
  • Windows PowerShell version:
Name                           Value
----                           -----
PSVersion                      7.4.0
PSEdition                      Core
GitCommitId                    7.4.0
OS                             Microsoft Windows 10.0.19045
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0
  • Chocolatey CLI Version: 2.2.2
  • Chocolatey Licensed Extension version: N/A - 0 packages installed.
  • Chocolatey License type: Open Source
  • Terminal/Emulator: PowerShell Core 7.4.0 on Microsoft Terminal 1.18.3181.0

Installed Packages

au 2022.10.24
chocolatey 2.2.2
chocolatey-compatibility.extension 1.0.0
chocolatey-core.extension 1.4.0
chocolatey-windowsupdate.extension 1.0.5
dellcommandupdate 5.1.0
delta 0.16.5
DotNet4.6.1 4.6.1055.20170308
Firefox 120.0.0
git.install 2.43.0
gnupg 2.4.3
KB2919355 1.0.20160915
KB2919442 1.0.20160915
KB2999226 1.0.20181019
KB3033929 1.0.5
KB3035131 1.0.3
KB3118401 1.0.5
kdeconnect-kde 23.4.0
keepassxc 2.7.6
libreoffice-still 7.5.8
microsoft-windows-terminal 1.18.3181
miktex.install 23.10.12
neovim 0.9.4
nodejs-lts 20.10.0
notepadplusplus.install 8.6.0
PIME 1.3.0
powershell-core 7.4.0
python39 3.9.13.20230703
ripgrep 13.0.0.20220913
syncthingtray 1.4.9
synergy 1.14.6.19
texstudio.install 4.6.3
vcredist140 14.38.33130
vcredist2015 14.0.24215.20170201

Output Log

(N/A - not sure how to get logs for tab completion)

Additional Context

The cause is that Chocolatey only defines TabExpansion function to enable completions, while that function is no longer used by PowerShell 7.4 [1]. A solution might be adding TabExpansion2 function (supported since PowerShell 3) besides the existing TabExpansion function, so that all users with PowerShell 2 ~ 7.4 can have Chocolatey tab completions.

As a side note, if TabExpansion2 is added, #2255 will likely be solved as well.

[1] PowerShell/PowerShell#18337

@silverqx
Copy link

silverqx commented Dec 5, 2023

I can confirm this, after upgrading to PS 7.4 tab completion stopped working and I can't get it working anymore.

@silverqx
Copy link

silverqx commented Dec 6, 2023

@yan12125 Do you know how to quickly workaround this? I tried to rename all occurrences to TabExpansion2 but it didn't help, is even possible to quickly workaround it?

@yan12125
Copy link
Author

yan12125 commented Dec 6, 2023

Do you know how to quickly workaround this?

I downgraded PowerShell.

@silverqx
Copy link

silverqx commented Dec 6, 2023

I want to have the latest 7.4 version so that's not what I want to do, I can live for some time w/o these completions.

It looks like the fix should be easy, I don't see anything that can cause bigger problems, and the code doesn't hardly depend on anything what was remove by the TabExpansion PR.

It looks like the problem is how things are exported or something?, but I looked to this only 5mins so I can be wrong.

@yan12125
Copy link
Author

yan12125 commented Jan 5, 2024

I'm not sure if there is an easy fix. PowerShell < 7.4 calls the TabExpansion function provided by Chocolatey for custom tab completions, and that function is no longer called by PowerShell > 7.4 as indicated in the aforementionef PR. A proper fix is probably using newer completion APIs like Register-ArgumentCompleter.

Scoop has a similar issue (Moeologist/scoop-completion#35), and a relevant fix (Moeologist/scoop-completion#25) is quite large.

@silverqx
Copy link

silverqx commented Jan 5, 2024

That aren't a good news, it looks like they will need to rewrite it to make it work again, what means we will be some time without tab completion 😞

@mklement0
Copy link
Contributor

mklement0 commented Jan 17, 2024

For background information, see PowerShell/PowerShell#20930 (comment)

Here's a solution that works in v7.4+:

  • For those looking to apply an immediate fix, they can apply it as follows:

    • Open the relevant file for editing:

      Invoke-Item "$env:ChocolateyInstall\helpers\ChocolateyTabExpansion.ps1"
      
    • Paste the code below at the end, save, and then start a new session.

      • Caveat: Since this obviously breaks the signature of said file, this will only work if your script-execution policy is set to not require local scripts to be signed.
  • To fix the problem at the source:

    • Add the code below to https://github.com/chocolatey/choco/blob/develop/src/chocolatey.resources/helpers/ChocolateyTabExpansion.ps1

    • If PowerShell versions prior to Windows PowerShell v5.1 do not need to be supported anymore:

      • Remove the following, then-obsolete code:
        • if (Test-Path Function:\TabExpansion) {
          Rename-Item Function:\TabExpansion TabExpansionBackup
          }
          function TabExpansion($line, $lastWord) {
          $lastBlock = [System.Text.RegularExpressions.Regex]::Split($line, '[|;]')[-1].TrimStart()
          switch -regex ($lastBlock) {
          # Execute Chocolatey tab completion for all choco-related commands
          "^$(Get-AliasPattern choco) (.*)" {
          ChocolateyTabExpansion $lastBlock
          }
          # Fall back on existing tab expansion
          default {
          if (Test-Path Function:\TabExpansionBackup) {
          TabExpansionBackup $line $lastWord
          }
          }
          }
          }
    • If pre-v5.1 support is still needed:

      • Enclose the following code in if ($PSVersionTable.PSVersion -ge '7.4') { ... }
function script:Get-AliasNames($exe) {
    @($exe, "$exe.exe") + @(Get-Alias | Where-Object { $_.Definition -eq $exe } | Select-Object -Exp Name)
}

Register-ArgumentCompleter -Native -CommandName (Get-AliasNames choco) -ScriptBlock {
    param($wordToComplete, $commandAst, $cursorColumn)

    # NOTE:
    # * The stringified form of $commandAst is the command's own command line (irrespective of
    #   whether other statements are on the same line or whether it is part of a pipeline).
    # * However, trailing whitespace is trimmed in the string representation of $commandAst. 
    #   Therefore, when the actual command line ends in space(s), they must be added back
    #   so that ChocolateyTabExpansion recognizes the start of a new argument.
    $ownCommandLine = [string] $commandAst
    $ownCommandLine = $ownCommandLine.Substring(0, [Math]::Min($ownCommandLine.Length, $cursorColumn))
    $ownCommandLine += ' ' * ($cursorColumn - $ownCommandLine.Length)

    ChocolateyTabExpansion $ownCommandLine
}

@mklement0
Copy link
Contributor

I've fixed a problem in the code in the previous comment, which now makes it support intra-line expansions properly too - from what I can tell, this makes it suitable for a fix.

However, if you want to avoid having to modify the module, I've posted a polyfill that overrides the TabExpansion2 function only, and, if added to $PROFILE, should work with all modules that still rely on the legacy TabExpansion function - see PowerShell/PowerShell#20930 (comment)

@yan12125
Copy link
Author

Thank you very much for the update! I just need one more line in Register-ArgumentCompleter script block to make it work: (the line removed during a comment edit)

    param($wordToComplete, $commandAst, $cursorColumn)

Without this, the behavior is kind of different from chocolatey on older PowerShell. When I type choco out and hit the Tab key, an older PowerShell shows the complete subcommand choco outdated, while the Register-ArgumentCompleter approach shows choco --help.

@mklement0
Copy link
Contributor

mklement0 commented Jan 19, 2024

Ouch! Thank you for catching that, @yan12125 - code in the earlier comment is now updated (I had simply made a copy-paste error when only partially replacing the original code).

@pauby
Copy link
Member

pauby commented Jan 19, 2024

@mklement0 Are you able to submit a PR for the changes that you're making? It sounds like you have it very close to working universally across PS versions.

@mklement0
Copy link
Contributor

@pauby, I've submitted #3387 - hope that's good enough.

yan12125 pushed a commit to yan12125/choco that referenced this issue Jun 1, 2024
… v7.4+

Make export of legacy `TabExpansion` function conditional on the PowerShell version.
yan12125 pushed a commit to yan12125/choco that referenced this issue Jun 1, 2024
… v7.4+

By using the new Register-ArgumentCompleter API

TabExpansion is not used since PowerShell 7.4, so the export of legacy
`TabExpansion` function is made conditional on the PowerShell version.

diff --git a/src/chocolatey.resources/helpers/ChocolateyTabExpansion.ps1 b/src/chocolatey.resources/helpers/ChocolateyTabExpansion.ps1
index 149bcd08..6c1f164e 100644
--- a/src/chocolatey.resources/helpers/ChocolateyTabExpansion.ps1
+++ b/src/chocolatey.resources/helpers/ChocolateyTabExpansion.ps1
@@ -264,26 +264,50 @@ if ($PowerTab_RegisterTabExpansion) {
     return
 }

-if (Test-Path Function:\TabExpansion) {
-    Rename-Item Function:\TabExpansion TabExpansionBackup
-}
-
-function TabExpansion($line, $lastWord) {
-    $lastBlock = [System.Text.RegularExpressions.Regex]::Split($line, '[|;]')[-1].TrimStart()
+# PowerShell up to v7.3.x: use a custom TabExpansion function.
+if ($PSVersionTable.PSVersion.Major -lt 7 -or ($PSVersionTable.PSVersion.Major -eq 7 -and $PSVersionTable.PSVersion.Minor -lt 4)) {
+    if (Test-Path Function:\TabExpansion) {
+        Rename-Item Function:\TabExpansion TabExpansionBackup
+    }

-    switch -regex ($lastBlock) {
-        # Execute Chocolatey tab completion for all choco-related commands
-        "^$(Get-AliasPattern choco) (.*)" {
-            ChocolateyTabExpansion $lastBlock
-        }
+    function TabExpansion($line, $lastWord) {
+        $lastBlock = [System.Text.RegularExpressions.Regex]::Split($line, '[|;]')[-1].TrimStart()
+

-        # Fall back on existing tab expansion
-        default {
-            if (Test-Path Function:\TabExpansionBackup) {
-                TabExpansionBackup $line $lastWord
+        switch -regex ($lastBlock) {
+            # Execute Chocolatey tab completion for all choco-related commands
+            "^$(Get-AliasPattern choco) (.*)" {
+                ChocolateyTabExpansion $lastBlock
+            }
+
+            # Fall back on existing tab expansion
+            default {
+                if (Test-Path Function:\TabExpansionBackup) {
+                    TabExpansionBackup $line $lastWord
+                }
             }
         }
     }
+} else { # PowerShell v7.4+: use the Register-ArgumentCompleter cmdlet (PowerShell no longer calls TabExpansion)
+    function script:Get-AliasNames($exe) {
+        @($exe) + @(Get-Alias | Where-Object { $_.Definition -eq $exe } | Select-Object -Exp Name)
+    }
+
+    Register-ArgumentCompleter -Native -CommandName (Get-AliasNames choco) -ScriptBlock {
+        param($wordToComplete, $commandAst, $cursorColumn)
+
+        # NOTE:
+        # * The stringified form of $commandAst is the command's own command line (irrespective of
+        #   whether other statements are on the same line or whether it is part of a pipeline).
+        # * However, trailing whitespace is trimmed in the string representation of $commandAst.
+        #   Therefore, when the actual command line ends in space(s), they must be added back
+        #   so that ChocolateyTabExpansion recognizes the start of a new argument.
+        $ownCommandLine = [string] $commandAst
+        $ownCommandLine = $ownCommandLine.Substring(0, [Math]::Min($ownCommandLine.Length, $cursorColumn))
+        $ownCommandLine += ' ' * ($cursorColumn - $ownCommandLine.Length)
+
+        ChocolateyTabExpansion $ownCommandLine
+    }
 }

 # SIG # Begin signature block
diff --git a/src/chocolatey.resources/helpers/chocolateyProfile.psm1 b/src/chocolatey.resources/helpers/chocolateyProfile.psm1
index a9968117..252d60af 100644
--- a/src/chocolatey.resources/helpers/chocolateyProfile.psm1
+++ b/src/chocolatey.resources/helpers/chocolateyProfile.psm1
@@ -25,7 +25,11 @@ Import-Module "$thisDirectory\Chocolatey.PowerShell.dll" -Cmdlet "Get-Environmen
 . $thisDirectory\functions\Write-FunctionCallLogMessage.ps1
 . $thisDirectory\ChocolateyTabExpansion.ps1

-Export-ModuleMember -Alias refreshenv -Function 'Update-SessionEnvironment', 'TabExpansion'
+$funcs = @('Update-SessionEnvironment')
+if ($PSVersionTable.PSVersion.Major -lt 7 -or ($PSVersionTable.PSVersion.Major -eq 7 -and $PSVersionTable.PSVersion.Minor -lt 4)) {
+    $funcs += 'TabExpansion' # Legacy TabExpansion function is only used up to PowerShell v7.3.x
+}
+Export-ModuleMember -Alias refreshenv -Function $funcs

 # SIG # Begin signature block
 # MIInKwYJKoZIhvcNAQcCoIInHDCCJxgCAQExDzANBglghkgBZQMEAgEFADB5Bgor
yan12125 pushed a commit to yan12125/choco that referenced this issue Jun 1, 2024
… v7.4+

By using the new Register-ArgumentCompleter API

TabExpansion is not used since PowerShell 7.4, so the export of legacy
`TabExpansion` function is made conditional on the PowerShell version.
yan12125 pushed a commit to yan12125/choco that referenced this issue Jun 6, 2024
… v7.4+

By using the new Register-ArgumentCompleter API

TabExpansion is not used since PowerShell 7.4, so the export of legacy
`TabExpansion` function is made conditional on the PowerShell version.
@gep13 gep13 added this to the 2.4.0 milestone Jun 6, 2024
@vexx32 vexx32 self-assigned this Oct 28, 2024
vexx32 pushed a commit to yan12125/choco that referenced this issue Nov 1, 2024
… v7.4+

By using the new Register-ArgumentCompleter API

TabExpansion is not used since PowerShell 7.4, so the export of legacy
`TabExpansion` function is made conditional on the PowerShell version.
vexx32 added a commit to yan12125/choco that referenced this issue Nov 1, 2024
The newer method is available from v5 and up, so we have no need to wait
for v7.4+ to change over. This will also let us remove the fallback
earlier, once we no longer support PS <5.0.
corbob pushed a commit to yan12125/choco that referenced this issue Nov 4, 2024
… v7.4+

By using the new Register-ArgumentCompleter API

TabExpansion is not used since PowerShell 7.4, so the export of legacy
`TabExpansion` function is made conditional on the PowerShell version.
corbob pushed a commit to yan12125/choco that referenced this issue Nov 4, 2024
The newer method is available from v5 and up, so we have no need to wait
for v7.4+ to change over. This will also let us remove the fallback
earlier, once we no longer support PS <5.0.
corbob added a commit that referenced this issue Nov 4, 2024
(#3364) Fix broken tab completion (expansion) in PowerShell v7.4+
@corbob corbob added 4 - Done and removed 3 - Review labels Nov 4, 2024
vexx32 added a commit to vexx32/choco that referenced this issue Nov 6, 2024
These tests are verifying the old method and are targeting an
implementation detail, rather than actually checking the tab completion
results, which the following tests do anyway.

As such, we're best off skipping them as there is no direct way to check
the same things for the Register-ArgumentCompleter method. The tests for
the tab completion results themselves covers this adequately already.
vexx32 added a commit to vexx32/choco that referenced this issue Nov 6, 2024
These tests are verifying the old method and are targeting an
implementation detail, rather than actually checking the tab completion
results, which the following tests do anyway.

As such, we're best off removingthem as there is no direct way to check
the same things for the Register-ArgumentCompleter method. The tests for
the tab completion results themselves covers this adequately already.
vexx32 added a commit to vexx32/choco that referenced this issue Nov 6, 2024
These tests are verifying the old method and are targeting an
implementation detail, rather than actually checking the tab completion
results, which the following tests do anyway.

As such, we're best off removing them as there is no direct way to check
the same things for the Register-ArgumentCompleter method. The tests for
the tab completion results themselves cover this adequately already.
corbob added a commit that referenced this issue Nov 6, 2024
(#3364) Remove unusable tab completion tests
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment