mikebattista / PowerShell-WSL-Interop

Integrate Linux commands into Windows with PowerShell and the Windows Subsystem for Linux (WSL).
https://devblogs.microsoft.com/commandline/integrate-linux-commands-into-windows-with-powershell-and-the-windows-subsystem-for-linux/
MIT License
433 stars 15 forks source link

Completion not working for default shells other than bash #26

Closed scrouthtv closed 4 years ago

scrouthtv commented 4 years ago

If I'm not mistaken, the readme claims that by importing a wsl command both command and argument completion is enabled automatically for pwsh as well:

To Reproduce Steps to reproduce the behavior:

  1. Run pwsh:
    
    PowerShell 7.0.0
    Copyright (c) Microsoft Corporation. All rights reserved.

https://aka.ms/powershell Type 'help' to get help.

PS C:\Users\x> pwsh --version PowerShell 7.0.0

2. Select a `wsl` that has completion enabled in `wsl bash`:

[x@DESKTOP-04NUF17 ~]$ echo $0 bash [x@DESKTOP-04NUF17 ~]$ git add blame cherry-pick config format-patch gui log.cmd pull remote restore show status worktree am branch citool describe fsck help merge push repack revert show-branch submodule apply bundle clean diff gc init mergetool range-diff replace rm sparse-checkout switch archive checkout clone difftool gitk instaweb mv rebase request-pull send-email stage tag bisect cherry commit fetch grep log notes reflog reset shortlog stash whatchanged [x@DESKTOP-04NUF17 ~]$ grep -- --after-context= --byte-offset --dereference-recursive --exclude-from= --fixed-strings --invert-match --max-count= --null-data --regexp= --basic-regexp --color --devices= --extended-regexp --help --label= --no-filename --only-matching --text --before-context= --colour --directories= --file= --ignore-case --line-buffered --no-ignore-case --perl-regexp --version --binary --context= --exclude= --files-with-matches --include= --line-number --no-messages --quiet --with-filename --binary-files= --count --exclude-dir= --files-without-match --initial-tab --line-regexp --null --recursive --word-regexp [lenni@DESKTOP-04NUF17 ~]$ ls -- --all --dereference-command-line-symlink-to-dir --hide-control-chars --numeric-uid-gid --tabsize= --almost-all --directory --human-readable --quote-name --time --author --dired --hyperlink --quoting-style= --time= --block-size= --escape --ignore= --recursive --time-style= --classify --file-type --ignore-backups --reverse --version --color --format= --indicator-style= --show-control-chars --width= --color= --full-time --inode --si --context --group-directories-first --kibibytes --size --dereference --help --literal --sort --dereference-command-line --hide= --no-group --sort=

3. Import the `wsl` command(s) into `pwsh`:

PS C:\Users\x> Import-WslCommand "git", "grep", "ls" PS C:\Users\x>

4. Completion does not work:

PS C:\Users\x> git ..bash_profile^C PS C:\Users\x> grep --^C PS C:\Users\x> ls --^C PS C:\Users\x>



**Expected behavior**
Tabbing on those commands in `pwsh` would provide me with the same completion commands as in `bash`.

**Desktop (please complete the following information):**
 - Windows 2004 (OS Build (19041.173)
 -  Arch Linux on Windows 10 x86_64 on 4.19.84-microsoft-standard
 - GNU bash, version 5.0.16(1)-release (x86_64-pc-linux-gnu)
mikebattista commented 4 years ago

Your expectation is correct. That should work and works for me.

Has this ever worked for you (so a regression)? Or is this the first time you're trying?

Could you attempt to <TAB> complete again with an imported command and then send me a few things?

scrouthtv commented 4 years ago

I just installed the module and it didn't work out of the box. Output:

PS C:\Users\x> gcm git, grep, ls                                                          

CommandType     Name                                               Version    Source          
-----------     ----                                               -------    ------          
Function        git                                                0.2.2      WslInterop      
Function        grep                                               0.2.2      WslInterop      
Function        ls                                                 0.2.2      WslInterop      

PS C:\Users\x> $WslCompletionFunctions                                                    

Name                           Value                                                          
----                           -----                                                          
git                            _minimal                                                       
grep                           _minimal                                                       

PS C:\Users\x> wsl -l                                                                     
Windows Subsystem for Linux Distributions:                                                    
Arch (Default)                                                                                
PS C:\Users\x> wsl apt list bash-completion                                               
zsh:1: command not found: apt                                                                 
PS C:\Users\x> wsl pacman -Qi bash-completion                                             
Name            : bash-completion                                                             
Version         : 2.10-1                                                                      
Description     : Programmable completion for the bash shell                                  
Architecture    : any                                                                         
URL             : https://github.com/scop/bash-completion                                     
Licenses        : GPL2                                                                        
Groups          : None                                                                        
Provides        : None                                                                        
Depends On      : bash                                                                        
Optional Deps   : None                                                                        
Required By     : None                                                                        
Optional For    : bash                                                                        
Conflicts With  : None                                                                        
Replaces        : None                                                                        
Installed Size  : 836.42 KiB                                                                  
Packager        : Bartłomiej Piotrowski <bpiotrowski@archlinux.org>                           
Build Date      : Thu 12 Dec 2019 09:54:58 PM CET                                             
Install Date    : Tue 14 Apr 2020 02:45:57 PM CEST                                            
Install Reason  : Explicitly installed                                                        
Install Script  : No                                                                          
Validated By    : Signature                                                                   

I'm using z shell if that's causing the trouble.

mikebattista commented 4 years ago

Thanks. Different distros are typically what complicates things. I recently updated the README to indicate the bash-completion package was a prerequisite, since not all distributions include it out of the box.

Based on the Install Date and Install Reason for the bash-completion package, it looks like you just installed it?

Have you tried argument completion again since installing that package?

With the bash-completion package installed, can you try $WslCompletionFunctions = @{ git = '__git_wrap__git_main'; grep = '_longopt'; ls = '_longopt' } and then <TAB> completing again?

scrouthtv commented 4 years ago

Actually I think my system time is messed up. I install bash-completion before I ran Import-WslCommand. Since then, I tried completion although it did not work.

However, by running your command, everything works fine:

PS C:\Users\x> Import-WslCommand "git", "grep", "ls"
PS C:\Users\x> gcm git, grep, ls

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Function        git                                                0.2.2      WslInterop
Function        grep                                               0.2.2      WslInterop
Function        ls                                                 0.2.2      WslInterop

PS C:\Users\x> $WslCompletionFunctions = @{ git = '__git_wrap__git_main'; grep = '_longopt'; ls = '_longopt' }
PS C:\Users\x> $WslCompletionFunctions

Name                           Value
----                           -----
ls                             _longopt
git                            __git_wrap__git_main
grep                           _longopt

PS C:\Users\x> git add^C
PS C:\Users\x> grep --after-context=^C
PS C:\Users\x> ls --all^C

I am currently trying completion with bash as my default shell, but give me two more minutes for a result.

scrouthtv commented 4 years ago

I somehow managed to get

less -<TAB>--------------------------------------------------c^C
scrouthtv commented 4 years ago

Nope, even with bash as my default shell tab completion is not imported.

scrouthtv commented 4 years ago
PS C:\Users\x> wsl sh -c "env | grep SHELL"
SHELL=/bin/bash
PS C:\Users\x> gcm git
Get-Command: The term 'git' is not recognized as the name of a cmdlet, function, script file, or operable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
PS C:\Users\x> Import-WslCommand "git"
PS C:\Users\x> git <TAB>.bash_profile
mikebattista commented 4 years ago

Ok. Based on your original output from $WslCompletionFunctions, I'd recommend also resetting the completion function cache, since it may have been populated from attempts to autocomplete before the bash-completion package was installed.

Let me know how it goes. The fact that you were able to autocomplete when $WslCompletionFunctions was populated correctly indicates there may be an issue with how this is populated.

If there is something I should capture in the README to better support z shell, I would like to figure out what that is and add it.

scrouthtv commented 4 years ago

Doesn't look too good:

PS C:\Users\x> wsl sh -c "env | grep SHELL"
SHELL=/bin/bash
PS C:\Users\x> ri "$Env:APPDATA\PowerShell WSL Interop\WslCompletionFunctions"
PS C:\Users\x> Import-WslCommand "git", "grep", "ls"
PS C:\Users\x> echo $WslCompletionFunctions
PS C:\Users\x>

I always tried these with a new instance of pwsh. Profile is completely empyt too.

scrouthtv commented 4 years ago

https://pastebin.com/W3f3xdum These are the files bash-completion provides on Arch, if there is anything missing.

scrouthtv commented 4 years ago

I guess you retrieve the completion function using

# Try to find the completion function.
$global:WslCompletionFunctions[$command] = wsl.exe (". /usr/share/bash-completion/bash_completion 2> /dev/null; __load_completion $command 2> /dev/null; complete -p $command 2> /dev/null | sed -E 's/^complete.*-F ([^ ]+).*`$/\1/'" -split ' ')

# If we can't find a completion function, default to _minimal which will resolve Linux file paths.
if ($null -eq $global:WslCompletionFunctions[$command] -or $global:WslCompletionFunctions[$command] -like "complete*") {
    $global:WslCompletionFunctions[$command] = "_minimal"
}

# Update the bash completion function cache.
New-Item $WslCompletionFunctionsCache -Force | Out-Null
$global:WslCompletionFunctions | Export-Clixml $WslCompletionFunctionsCache

(WslInterop.psm1, 3a5c1b7, line 123 - 135)

When I only run the very first wsl command, it works how (I guess) it is supposed to:

PS C:\Users\x> $command="grep"
PS C:\Users\x> echo $command
grep
PS C:\Users\x> wsl.exe (". /usr/share/bash-completion/bash_completion 2> /dev/null; __load_completion $command 2> /dev/null; complete -p $command 2> /dev/null | sed -E 's/^complete.*-F ([^ ]+).*`$/\1/'" -split ' ')
_longopt
PS C:\Users\x>
scrouthtv commented 4 years ago

Both ls and git are also correctly reported.

mikebattista commented 4 years ago

Can you run through my steps exactly as I shared them?

You should restart PowerShell after clearing the APPDATA cache. Then you also need to attempt autocompletion before calling $WslCompletionFunctions, otherwise no output would be expected.

scrouthtv commented 4 years ago

Doesn't look too good:

PS C:\Users\x> wsl sh -c "env | grep SHELL"
SHELL=/bin/bash
PS C:\Users\x> ri "$Env:APPDATA\PowerShell WSL Interop\WslCompletionFunctions"
PS C:\Users\x> Import-WslCommand "git", "grep", "ls"
PS C:\Users\x> echo $WslCompletionFunctions
PS C:\Users\x>

I always tried these with a new instance of pwsh. Profile is completely empyt too.

mikebattista commented 4 years ago

I don't see you attempting to <TAB> complete between importing the commands and outputting $WslCompletionFunctions.

scrouthtv commented 4 years ago

Oh sorry, my bad. Now the fun begins: I tried this with bash as in above:

PS C:\Users\x> wsl sh -c "env | grep SHELL"
SHELL=/bin/bash
PS C:\Users\x> ri "$Env:APPDATA\PowerShell WSL Interop\WslCompletionFunctions"
Remove-Item: Cannot find path 'C:\Users\x\AppData\Roaming\PowerShell WSL Interop\WslCompletionFunctions' because it does not exist.
PS C:\Users\x> Import-WslCommand "git", "grep", "ls"
PS C:\Users\x> git <TAB>add^C
PS C:\Users\x> echo $WslCompletionFunctions

Name                           Value
----                           -----
git                            __git_wrap__git_main

PS C:\Users\x>

However, with zsh:

PS C:\Users\x> wsl sh -c "env | grep SHELL"
SHELL=/usr/bin/zsh
PS C:\Users\x> ri "$Env:APPDATA\PowerShell WSL Interop\WslCompletionFunctions"
Remove-Item: Cannot find path 'C:\Users\x\AppData\Roaming\PowerShell WSL Interop\WslCompletionFunctions' because it does not exist.
PS C:\Users\x> Import-WslCommand "git", "grep", "ls"
PS C:\Users\x> git <TAB>.\.bash_history^C
PS C:\Users\x> echo $WslCompletionFunctions

Name                           Value
----                           -----
git                            _minimal

PS C:\Users\x>
scrouthtv commented 4 years ago

This is because bash has a command __load_completion which is used to retrieve the completion for a given $command. However, zsh does not have this command:

> __load_completion grep
zsh: command not found: __load_completion
mikebattista commented 4 years ago

Ok. Thanks. So sounds like at least bash is working as expected now.

So __load_completion is defined within /usr/share/bash-completion/bash_completion which I dot source to identify completion functions, as you saw in the code.

Is there a different way to source this file for z shell so __load_completion would be defined?

scrouthtv commented 4 years ago

Suggestion: Change WslInterop.psm1 line 125 to

 wsl.exe bash -c ". /usr/share/bash-completion/bash_completion 2> /dev/null; __load_completion $command 2> /dev/null; complete -p $command 2> /dev/null | sed -E 's/^complete.*-F ([^ ]+).*`$/\1/'" -split ' '

For example:

PS C:\Users\x> wsl sh -c "env | grep SHELL"
PS C:\Users\x> $command="grep"
PS C:\Users\x> wsl.exe (". /usr/share/bash-completion/bash_completion 2> /dev/null; __load_completion $command 2> /dev/null; complete -p $command 2> /dev/null | sed -E 's/^complete.*-F ([^ ]+).*`$/\1/'" -split ' ')
PS C:\Users\x> wsl.exe bash -c ". /usr/share/bash-completion/bash_completion 2> /dev/null; __load_completion $command 2> /dev/null; complete -p $command 2> /dev/null | sed -E 's/^complete.*-F ([^ ]+).*`$/\1/'" -split ' '
_longopt
PS C:\Users\x> 
scrouthtv commented 4 years ago

I don't really know how sourcing a bash completion function would work in zsh so using bash instead seems to be the easiest (and fastest) way.

mikebattista commented 4 years ago

I can try that.

Actually I think my system time is messed up. I install bash-completion before I ran Import-WslCommand. Since then, I tried completion although it did not work.

However, by running your command, everything works fine:

PS C:\Users\x> Import-WslCommand "git", "grep", "ls"
PS C:\Users\x> gcm git, grep, ls

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Function        git                                                0.2.2      WslInterop
Function        grep                                               0.2.2      WslInterop
Function        ls                                                 0.2.2      WslInterop

PS C:\Users\x> $WslCompletionFunctions = @{ git = '__git_wrap__git_main'; grep = '_longopt'; ls = '_longopt' }
PS C:\Users\x> $WslCompletionFunctions

Name                           Value
----                           -----
ls                             _longopt
git                            __git_wrap__git_main
grep                           _longopt

PS C:\Users\x> git add^C
PS C:\Users\x> grep --after-context=^C
PS C:\Users\x> ls --all^C

I am currently trying completion with bash as my default shell, but give me two more minutes for a result.

Can you confirm here that you were using z shell when you set $WslCompletionFunctions = @{ git = '__git_wrap__git_main'; grep = '_longopt'; ls = '_longopt' } manually and autocompletion worked?

I want to make sure that, assuming $WslCompletionFunctions is populated correctly, autocompletion would in fact work with z shell.

scrouthtv commented 4 years ago

Sorry for not answering your question. In the retried the test you mentioned with z shell and even with $WslCompletionFunctions set, completion does not work:

PS C:\Users\x> wsl sh -c "env | grep SHELL"
SHELL=/usr/bin/zsh
PS C:\Users\x> Import-WslCommand "git", "grep", "ls"
PS C:\Users\x> git .\.android\^C
PS C:\Users\x> $WslCompletionFunctions = @{ git = '__git_wrap__git_main'; grep = '_longopt'; ls = '_longopt' }
PS C:\Users\x> $WslCompletionFunctions

Name                           Value
----                           -----
git                            __git_wrap__git_main
ls                             _longopt
grep                           _longopt

PS C:\Users\x> git .\.bash_history^C
PS C:\Users\x> grep --<TAB>^C
PS C:\Users\x> ls --<TAB>^C

I guess there is a second command that converts the __git_wrap__git_main and _longopt at runtime to actual completions based on the context at runtime? This function involves wsl too but is not explicitly calling bash?

mikebattista commented 4 years ago

Thanks. It not working is what I would have expected given the dot sourcing issue, which is why I was surprised that it might have worked for you looking back at the history of the repros.

Calling bash explicitly both when identifying completion functions and generating completions is the right approach. Now I'm just battling with WSL not accepting the completion generation command when I switch to bash -c.

mikebattista commented 4 years ago

@scrouthtv could you try your repro again with the latest code?

Everything works for me now with zsh set as my default shell.

scrouthtv commented 4 years ago

Yep works for me now, too. Thanks for your quick help!

mikebattista commented 4 years ago

Awesome. 0.2.3 was published with the fix. Thanks for reporting this.