Skip to content

Add PowerShell tab completion support#212

Open
HeyItsGilbert wants to merge 4 commits intoiterative:mainfrom
HeyItsGilbert:main
Open

Add PowerShell tab completion support#212
HeyItsGilbert wants to merge 4 commits intoiterative:mainfrom
HeyItsGilbert:main

Conversation

@HeyItsGilbert
Copy link

Summary

This PR adds PowerShell tab completion support to shtab, enabling native PowerShell argument completion for CLI applications built with argparse.

Key features:

  • Generates PowerShell completion scripts using Register-ArgumentCompleter -Native
  • Supports PowerShell 5.1+ (Windows Desktop) and PowerShell Core 7.x (cross-platform)
  • Automatically discovers subcommands, options, choices, and file/directory completions
  • Follows the same architecture as existing bash/zsh/tcsh support
  • Includes examples and comprehensive documentation

Changes

  • shtab/init.py: Added PowerShell completer implementation with:

    • get_powershell_commands() for parser tree extraction
    • complete_powershell() decorated completer function
    • PowerShell script template with state machine for completion dispatch
    • Serialization helpers for PowerShell hashtable generation
    • Integration with existing CHOICE_FUNCTIONS infrastructure
  • Documentation & Examples:

    • Updated README.rst and docs/index.md to list PowerShell as supported shell
    • Added PowerShell installation & usage guide in docs/use.md
    • Updated examples/customcomplete.py with PowerShell preamble
  • Tests: Extended existing parametrized test suite to include PowerShell

Building & Testing Locally

Prerequisites

pip install -e .

Run tests

# All tests (including PowerShell smoke tests)
pytest tests/test_shtab.py -v

# PowerShell-only tests (requires pwsh)
pytest tests/test_shtab.py -k "powershell" -v

Generate completion script manually

# For shtab itself
shtab --shell=powershell shtab > shtab.ps1

# Or for an example app
python -c "from examples.customcomplete import main; print(main)" | shtab --shell=powershell > custom.ps1

Test completion in PowerShell

# Load the generated script
. ./shtab.ps1

# Test completions (Ctrl+Space or type -<TAB>)
shtab -<TAB>

Enable debug output

Set the SHTAB_DEBUG environment variable to trace completion state:

$env:SHTAB_DEBUG=1
. ./shtab.ps1

HeyItsGilbert and others added 2 commits February 14, 2026 02:25
* Add PowerShell tab completion support

Implement PowerShell as a fourth supported shell alongside bash, zsh, and
tcsh. The generated script uses Register-ArgumentCompleter -Native with a
ScriptBlock that parses the command AST, tracks parser state (subcommands,
options, positionals, nargs), and returns CompletionResult objects.

Key changes:
- Add complete_powershell() with @mark_completer("powershell") decorator
- Add get_powershell_commands() for recursive parser traversal
- Add PowerShell serialization helpers for hashtables/arrays
- Add CHOICE_FUNCTIONS entries for powershell file/dir completion
- Update examples/customcomplete.py with PowerShell preamble
- Update tests with PowerShell assertions (all 78 tests pass)
- Update README, docs/index.md, docs/use.md with PowerShell instructions
- Include PLAN-powershell-support.md documenting the architecture

Note: option-specific choices/compgens completion is stubbed with a TODO
marker for future implementation.

https://claude.ai/code/session_01F9QZN9wfoaU7gujvy9Kva9

* Implement option-specific choices/compgens in PowerShell completer

Track a $currentActionKey through the state machine that gets updated
on subparser reset, option match, and nargs exhaustion. Use it in the
completion generation section so that option-specific choices (e.g.
--shell bash/zsh/tcsh/powershell) and compgen functions are properly
completed, matching the bash implementation's behavior.

https://claude.ai/code/session_01F9QZN9wfoaU7gujvy9Kva9

* Add SHTAB_DEBUG env var for PowerShell completion debugging

When $env:SHTAB_DEBUG is set to 'true', the completer prints
the internal state (wordToComplete, prefix, actionKey, etc.)
to help diagnose completion issues.

https://claude.ai/code/session_01F9QZN9wfoaU7gujvy9Kva9

---------

Co-authored-by: Claude <noreply@anthropic.com>
@HeyItsGilbert HeyItsGilbert changed the title Add PowerShell tab completion support (#1) Add PowerShell tab completion support Feb 14, 2026
Copy link

@pmhahn pmhahn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just had a short look. Looks okay mostly, but someone with PowerShell knowledge should have a look at the embedded PS1 code.

Found some minor nits.

Maybe split the large __init__.py into individual files in the future? Or at least move the embedded shell-scripts parts into individual files? See #199 ?



def _powershell_escape(string: str) -> str:
"""Escape a string for use inside a PowerShell single-quoted string."""
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I would prefer that _ps1_escape adds the surrounding single-quotes itself. I'm no PS1 expert myself, but at least with bash there are several ways to escape strings

  • in single-quotes only single-quotes must be handles special
  • in double-quotes several characters still need backslash escaping
  • without quote many characters must be backslash escaped

With bash shlex.quote() could switch between those choices or even depend on the string to escape and we don't care which is uses as it is an internal details.

With your _ps1_escape here you always must use single quotes and we later can't change it if we find something better.

return "@{\n" + "\n".join(entries) + "\n}"


def get_powershell_commands(root_parser, root_prefix, choice_functions=None):
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh oh, I see lots of get_bash_commands() code here. There are many differences, but I don't have time to dig in right now to find out, why they differ.

Maybe someone™ should refactor this and to extract common code? 🤔

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need this addressed in this PR?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, but a mental note to whoever to address this soon.

HeyItsGilbert and others added 2 commits March 11, 2026 16:54
Thank you for the code suggestions! I'll get the rest fixed shortly. Gotta remember to keep things pythonic :P

Co-authored-by: Philipp Hahn <pmhahn+github@pmhahn.de>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants