argcomplete
is a great Python package for
tab completion, however, it only works with bash as your shell (and with zsh, fish and
tcsh with limited support). PowerShell
has also a great command-line interface, but the argcomplete
-based applications cannot
use its tab completion functionality.
This project provides a straightforward solution how to use argcomplete
tab completion
functionality in PowerShell
.
psamwe.py
Example script withargcomplete
.psamwe.complete.ps1
Script to registerpsamwe
command for tab completion.psamwe.complete.psm1
Module to registerpsamwe
command for tab completion.
- Start a new PowerShell window.
- Clone this project.
- Create and activate your Python virtual environment as you like.
- Enter into this project's directory (
cd powershell-argcomplete-mwe
) pip install --editable .
- Activate PowerShell tab completion with either of the scripts:
- Dot-sourcing:
. .\psamwe.complete.ps1
- Import module:
Import-Module .\psamwe.complete.psm1
- Dot-sourcing:
- Type
psamwe
- Play with
Tab
orCtrl+Space
auto-completion ofpsamwe
Register-ArgumentCompleter
(link)
"registers a custom argument completer. An argument completer allows you to provide
dynamic tab completion, at run time for any command that you specify." The main idea
is that using this cmdlete we register a script to complete arguments of an alias
function which wraps the original command:
- A PowerShell function is defined which is a simple alias of the original command:
$PsamwePythonCommand = "&'python .\psamwe.py'"
$PsamweCommandAlias = "psamwe"
Function psamwe {
Invoke-Expression "$PsamwePythonCommand $args"
}
- Using
Register-ArgumentCompleter
, the command alias is registered for argument completion:
Register-ArgumentCompleter -Native -CommandName $PsamweCommandAlias -ScriptBlock $PsamweArgCompleteScriptBlock
$PsamweArgCompleteScriptBlock
script block is called by activating argument completion
(by pressing Tab
or Ctrl+Space
)
$PsamweArgCompleteScriptBlock
script block mimics bash by setting up special environment variables on a similiar way:
New-Item -Path Env: -Name _ARGCOMPLETE -Value 1 | Out-Null # Enables tab completion in argcomplete
New-Item -Path Env: -Name COMP_TYPE -Value 9 | Out-Null # Constant
New-Item -Path Env: -Name _ARGCOMPLETE_IFS -Value " " | Out-Null # Separator of the items
New-Item -Path Env: -Name _ARGCOMPLETE_SUPPRESS_SPACE -Value 1 | Out-Null # Constant
New-Item -Path Env: -Name _ARGCOMPLETE_COMP_WORDBREAKS -Value "" | Out-Null # Constant
New-Item -Path Env: -Name COMP_POINT -Value $cursorPosition | Out-Null # Refers to the last character of the current line
New-Item -Path Env: -Name COMP_LINE -Value $line | Out-Null # Current line
argcomplete
uses these variables as input to determine the completion suggestions.
argcomplete
writes the result into an output stream. Per default, it uses file descriptor8
which is not supported by Powershell. Instead,psamwe.py
scripts changes the output stream tostdout
. However, this change would break the bash experience. Therefore, a new environment variable has been introduced to specify that the completion is triggered by PowerShell:
New-Item -Path Env: -Name _ARGCOMPLETE_POWERSHELL -Value 1 | Out-Null
This environment variable is used in psamwe.py
:
output_stream = None
if "_ARGCOMPLETE_POWERSHELL" in os.environ:
output_stream = codecs.getwriter("utf-8")(sys.stdout.buffer)
argcomplete.autocomplete(parser, output_stream=output_stream)
- Finally, the script just executes the original command. Due to the set environment
variables,
argcomplete
will execute theautocomplete
function and writes the result back tostdout
which is redirected into a variable. Splitting this result into separated lines are the results of argument completion which are presented by the command-line window to the user:
Invoke-Expression $PsamwePythonCommand -OutVariable completionResult -ErrorVariable errorOut -ErrorAction SilentlyContinue | Out-Null
...
$items = $completionResult.Split()
if ($items -eq $completionResult) {
"$items "
}
else {
$items
}
- There are a few minor tricks in the pre- and post-processing parts to mimic the bash experience as much as possible. These can be freely modified to adapt the behaviour to specific needs.