apple / swift-argument-parser

Straightforward, type-safe argument parsing for Swift
Apache License 2.0
3.26k stars 307 forks source link

Zsh shell completion is broken when short flags exist #631

Open bartekpacia opened 3 months ago

bartekpacia commented 3 months ago

Hi,

This is my first issue in this repository. Thank you for creating and maintaining this great package!

Now onto the problem: I discovered a bug with shell completions. Please see video below:

https://github.com/apple/swift-argument-parser/assets/40357511/6cdeeb81-0ed8-43ee-b0a3-e26780f56540

ArgumentParser version:

1.3.1

Swift version:

$ swift --version
swift-driver version: 1.90.11.1 Apple Swift version 5.10 (swiftlang-5.10.0.13 clang-1500.3.9.4)
Target: arm64-apple-macosx14.0

Checklist

Steps to Reproduce

Use Zsh with shell completion enabled.

Then create a simple Swift command line app that uses swift-argument-parser.

├── Package.resolved
├── Package.swift
├── Sources
│   ├── Example.swift
│   └── Root.swift
Package.swift ```swift // swift-tools-version: 5.9 import PackageDescription let package = Package( name: "SAPbug", dependencies: [ .package(url: "https://github.com/apple/swift-argument-parser", from: "1.0.0"), ], targets: [ .executableTarget( name: "SAPbug", dependencies: [ .product(name: "ArgumentParser", package: "swift-argument-parser"), ], path: "Sources"), ] ) ```
Sources/Root.swift ```swift import ArgumentParser @main struct Root: ParsableCommand { static var configuration = CommandConfiguration( commandName: "SAPbug", version: "1.0.0", subcommands: [Example.self] ) @Option(help: ArgumentHelp("Some caches option")) var entries: String = "caches" mutating func run() throws { print("Root run") } } ```
Sources/Example.swift ```swift import ArgumentParser struct Example: ParsableCommand { static var configuration = CommandConfiguration( abstract: "Some example command to demo bug.", helpNames: [.long, .customShort("h")] ) @Option( help: ArgumentHelp( "Entries to remove: \"caches\" targets OCI and IPSW caches and \"vms\" targets local VMs." ) ) var entries: String = "caches" @Option( help: ArgumentHelp( "Remove entries that were last accessed more than n days ago", valueName: "n" ) ) var olderThan: UInt? @Option(help: .hidden) var cacheBudget: UInt? @Option( help: ArgumentHelp( "Remove the least recently used entries that do not fit the specified space size budget n, expressed in gigabytes", valueName: "n" ) ) var spaceBudget: UInt? @Flag() var gc: Bool = false mutating func run() throws { print("Example run") } } ```

Then build the executable:

swift build

Then source shell completions into FPATH. In my case I do:

./.build/debug/SAPbug --generate-completion-script zsh > /opt/homebrew/share/zsh/site-functions/_SAPbug && exec zsh

Then run ./.build/debug/SAPbug and try triggering various shell completions (see video).

Expected behavior

$ ./.build/debug/SAPbug example [tab]
--entries       -- Entries to remove: "caches" targets OCI and IPSW caches and "vms" targets local VMs.
--help          -- Show help information.
--older-than    -- Remove entries that were last accessed more than n days ago
--space-budget  -- Remove the least recently used entries that do not fit the specified space size budget n, expressed
--version       -- Show the version.
--gc

Short options should not be suggested, or alternatively, they should not break in such a bad way.

Actual behavior

Completions are broken.

Command invocation ``` $ ./.build/debug/SAPbug example [tab] --entries --help --older-than --space-budget --version -h -- Entries to remove: "caches" targets OCI and IPSW caches and "vms" targets local VMs. -- Show help information. -- Remove entries that were last accessed more than n days ago -- Remove the least recently used entries that do not fit the specified space size budget n, express -- Show the version. --entries --help --older-than --space-budget --version -h -- Entries to remove: "caches" targets OCI and IPSW caches and "vms" targets local VMs. -- Show help information. -- Remove entries that were last accessed more than n days ago -- Remove the least recently used entries that do not fit the specified space size budget n, express -- Show the version. --gc ```
natecook1000 commented 2 months ago

@bartekpacia Thanks for this report! I've heard of this issue from some others who have oh-my-zsh installed, but I haven't been able to reproduce this myself, even after installing that tool. Do you see this behavior from any other completion scripts that aren't generated by ArgumentParser? Does git auto-complete correctly for you?

bartekpacia commented 1 month ago

Thanks for the response @natecook1000!

I tried completing short flags for git, as you suggested, and I reproduced it:

https://github.com/apple/swift-argument-parser/assets/40357511/1af8ea16-c747-4043-99d7-36b0ddc97e57

Here's my ~/.zshrc: https://github.com/bartekpacia/dotfiles/blob/45c01f3c76c1370897a6bb6e7e5508e2c06079d0/dot/zshrc