Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ struct BashCompletionsGenerator {
// that other command functions don't need.
if isRootCommand {
result += """
export \(CompletionShell.environmentVariableName)=bash
export \(CompletionShell.shellEnvironmentVariableName)=bash
\(CompletionShell.shellVersionEnvironmentVariableName)="$(IFS='.'; printf %s "${BASH_VERSINFO[*]}")"
export \(CompletionShell.shellVersionEnvironmentVariableName)
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
COMPREPLY=()
Expand Down
22 changes: 18 additions & 4 deletions Sources/ArgumentParser/Completions/CompletionsGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,32 @@ public struct CompletionShell: RawRepresentable, Hashable, CaseIterable {
}

/// While generating a shell completion script or while a Swift custom completion
/// function is executing to offer completions for a word from a command line
/// (e.g., when `customCompletion` from `@Option(completion: .custom(customCompletion))`
/// function is executing to offer completions for a word from a command line (e.g.,
/// while `customCompletion` from `@Option(completion: .custom(customCompletion))`
/// executes), an instance representing the shell for which completions will
/// be or are being requested, respectively. Otherwise `nil`.
public internal(set) static var requesting: CompletionShell?

/// While a Swift custom completion function is executing to offer completions
/// for a word from a command line (e.g., while `customCompletion` from
/// `@Option(completion: .custom(customCompletion))` executes), a `String`
/// representing the version of the shell for which completions are being
/// requested. Otherwise `nil`.
public internal(set) static var requestingVersion: String?

/// The name of the environment variable whose value is the name of the shell
/// for which completions are being requested from a custom completion
/// handler.
///
/// The environment variable is set in generated completion scripts.
static let environmentVariableName = "SAP_SHELL"
static let shellEnvironmentVariableName = "SAP_SHELL"

/// The name of the environment variable whose value is the version of the
/// shell for which completions are being requested from a custom completion
/// handler.
///
/// The environment variable is set in generated completion scripts.
static let shellVersionEnvironmentVariableName = "SAP_SHELL_VERSION"
}

struct CompletionsGenerator {
Expand All @@ -81,7 +95,7 @@ struct CompletionsGenerator {
}
}

/// Generates a Bash completion script for this generators shell and command..
/// Generates a shell completion script for this generator's shell and command.
func generateCompletionScript() -> String {
CompletionShell.requesting = shell
switch shell {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@ extension FishCompletionsGenerator {
let preprocessorFunctionName = preprocessorFunctionName(commandName: commandName)
return """
function \(functionName)
set -gx \(CompletionShell.environmentVariableName) fish
set -gx \(CompletionShell.shellEnvironmentVariableName) fish
set -gx \(CompletionShell.shellVersionEnvironmentVariableName) "$FISH_VERSION"
set -l currentCommands (\(preprocessorFunctionName) (commandline -opc))
set -l expectedCommands (string split \"\(separator)\" $argv[1])
set -l subcommands (string split \"\(separator)\" $argv[2])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,12 @@ struct ZshCompletionsGenerator {
}

let functionText = """
\(functionName)() {\(isRootCommand ? "\n export \(CompletionShell.environmentVariableName)=zsh" : "")
\(functionName)() {\(isRootCommand ? """

export \(CompletionShell.shellEnvironmentVariableName)=zsh
\(CompletionShell.shellVersionEnvironmentVariableName)="$(builtin emulate zsh -c 'printf %s "${ZSH_VERSION}"')"
export \(CompletionShell.shellVersionEnvironmentVariableName)
""" : "")
integer ret=1
local -a args
args+=(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,19 @@ In this example, when a user requests completions for the `--target` option, the

### Configuring Completion Candidates per Shell

The shells supported for parameter completion all have different completion candidate formats,
as well as their own different syntaxes and built-in commands.
The shells supported for word completion all have different completion candidate formats, as
well as their own different syntaxes and built-in commands.

The `CompletionShell.requesting` singleton (of type `CompletionShell?`) can be read to determine
which shell is requesting completion candidates when evaluating functions that either provide
arguments to a `CompletionKind` creation function, or that are themselves arguments to a
`CompletionKind` creation function. e.g.:
`CompletionKind` creation function.

The `CompletionShell.requestingVersion` singleton (of type `String?`) can be read to determine
the version of the shell that is requesting completion candidates when evaluating functions that
are themselves arguments to a `CompletionKind` creation function.

e.g.:

```swift
struct Tool {
Expand All @@ -96,10 +102,15 @@ func generateCommandPerShell() -> String {
}

/// Runs during completion while user is typing command line to use your tool
/// Note that the `Version` struct is not included in Swift Argument Parser
func generateCompletionCandidatesPerShell(_ arguments: [String]) -> [String] {
switch CompletionShell.requesting {
case CompletionShell.bash:
return ["A:in:bash:syntax", "B:in:bash:syntax", "C:in:bash:syntax"]
if Version(CompletionShell.requestingVersion).major >= 4 {
return ["A:in:bash4+:syntax", "B:in:bash4+:syntax", "C:in:bash4+:syntax"]
} else {
return ["A:in:bash:syntax", "B:in:bash:syntax", "C:in:bash:syntax"]
}
case CompletionShell.fish:
return ["A:in:fish:syntax", "B:in:bash:syntax", "C:in:bash:syntax"]
case CompletionShell.zsh:
Expand Down
4 changes: 3 additions & 1 deletion Sources/ArgumentParser/Parsing/CommandParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -361,10 +361,12 @@ extension CommandParser {
throw ParserError.invalidState
}

if let completionShellName = ProcessInfo.processInfo.environment[CompletionShell.environmentVariableName] {
if let completionShellName = ProcessInfo.processInfo.environment[CompletionShell.shellEnvironmentVariableName] {
CompletionShell.requesting = CompletionShell(rawValue: completionShellName)
}

CompletionShell.requestingVersion = ProcessInfo.processInfo.environment[CompletionShell.shellVersionEnvironmentVariableName]

// Parsing and retrieval successful! We don't want to continue with any
// other parsing here, so after printing the result of the completion
// function, exit with a success code.
Expand Down
5 changes: 5 additions & 0 deletions Tests/ArgumentParserExampleTests/MathExampleTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ private let bashCompletionScriptText = """

_math() {
export SAP_SHELL=bash
SAP_SHELL_VERSION="$(IFS='.'; printf %s "${BASH_VERSINFO[*]}")"
export SAP_SHELL_VERSION
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
COMPREPLY=()
Expand Down Expand Up @@ -397,6 +399,8 @@ typeset -A opt_args

_math() {
export SAP_SHELL=zsh
SAP_SHELL_VERSION="$(builtin emulate zsh -c 'printf %s "${ZSH_VERSION}"')"
export SAP_SHELL_VERSION
integer ret=1
local -a args
args+=(
Expand Down Expand Up @@ -586,6 +590,7 @@ end

function _swift_math_using_command
set -gx SAP_SHELL fish
set -gx SAP_SHELL_VERSION "$FISH_VERSION"
set -l currentCommands (_swift_math_preprocessor (commandline -opc))
set -l expectedCommands (string split \" \" $argv[1])
set -l subcommands (string split \" \" $argv[2])
Expand Down
12 changes: 12 additions & 0 deletions Tests/ArgumentParserUnitTests/CompletionScriptTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ typeset -A opt_args

_base-test() {
export SAP_SHELL=zsh
SAP_SHELL_VERSION="$(builtin emulate zsh -c 'printf %s "${ZSH_VERSION}"')"
export SAP_SHELL_VERSION
integer ret=1
local -a args
args+=(
Expand Down Expand Up @@ -260,6 +262,8 @@ private let bashBaseCompletions = """

_base_test() {
export SAP_SHELL=bash
SAP_SHELL_VERSION="$(IFS='.'; printf %s "${BASH_VERSINFO[*]}")"
export SAP_SHELL_VERSION
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
COMPREPLY=()
Expand Down Expand Up @@ -353,6 +357,8 @@ typeset -A opt_args

_escaped-command() {
export SAP_SHELL=zsh
SAP_SHELL_VERSION="$(builtin emulate zsh -c 'printf %s "${ZSH_VERSION}"')"
export SAP_SHELL_VERSION
integer ret=1
local -a args
args+=(
Expand Down Expand Up @@ -389,6 +395,7 @@ end

function _swift_base-test_using_command
set -gx SAP_SHELL fish
set -gx SAP_SHELL_VERSION "$FISH_VERSION"
set -l currentCommands (_swift_base-test_preprocessor (commandline -opc))
set -l expectedCommands (string split " " $argv[1])
set -l subcommands (string split " " $argv[2])
Expand Down Expand Up @@ -491,6 +498,8 @@ typeset -A opt_args

_parent() {
export SAP_SHELL=zsh
SAP_SHELL_VERSION="$(builtin emulate zsh -c 'printf %s "${ZSH_VERSION}"')"
export SAP_SHELL_VERSION
integer ret=1
local -a args
args+=(
Expand All @@ -515,6 +524,8 @@ let bashHiddenCompletion = """

_parent() {
export SAP_SHELL=bash
SAP_SHELL_VERSION="$(IFS='.'; printf %s "${BASH_VERSINFO[*]}")"
export SAP_SHELL_VERSION
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
COMPREPLY=()
Expand Down Expand Up @@ -545,6 +556,7 @@ end

function _swift_parent_using_command
set -gx SAP_SHELL fish
set -gx SAP_SHELL_VERSION "$FISH_VERSION"
set -l currentCommands (_swift_parent_preprocessor (commandline -opc))
set -l expectedCommands (string split " " $argv[1])
set -l subcommands (string split " " $argv[2])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@ typeset -A opt_args

_base-test() {
export SAP_SHELL=zsh
SAP_SHELL_VERSION="$(builtin emulate zsh -c 'printf %s "${ZSH_VERSION}"')"
export SAP_SHELL_VERSION
integer ret=1
local -a args
args+=(
Expand Down Expand Up @@ -298,6 +300,8 @@ let bashRequestingBaseCompletions = """

_base_test() {
export SAP_SHELL=bash
SAP_SHELL_VERSION="$(IFS='.'; printf %s "${BASH_VERSINFO[*]}")"
export SAP_SHELL_VERSION
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
COMPREPLY=()
Expand Down Expand Up @@ -391,6 +395,8 @@ typeset -A opt_args

_escaped-command() {
export SAP_SHELL=zsh
SAP_SHELL_VERSION="$(builtin emulate zsh -c 'printf %s "${ZSH_VERSION}"')"
export SAP_SHELL_VERSION
integer ret=1
local -a args
args+=(
Expand Down Expand Up @@ -427,6 +433,7 @@ end

function _swift_base-test_using_command
set -gx SAP_SHELL fish
set -gx SAP_SHELL_VERSION "$FISH_VERSION"
set -l currentCommands (_swift_base-test_preprocessor (commandline -opc))
set -l expectedCommands (string split " " $argv[1])
set -l subcommands (string split " " $argv[2])
Expand Down Expand Up @@ -529,6 +536,8 @@ typeset -A opt_args

_parent() {
export SAP_SHELL=zsh
SAP_SHELL_VERSION="$(builtin emulate zsh -c 'printf %s "${ZSH_VERSION}"')"
export SAP_SHELL_VERSION
integer ret=1
local -a args
args+=(
Expand All @@ -553,6 +562,8 @@ let bashRequestingHiddenCompletion = """

_parent() {
export SAP_SHELL=bash
SAP_SHELL_VERSION="$(IFS='.'; printf %s "${BASH_VERSINFO[*]}")"
export SAP_SHELL_VERSION
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
COMPREPLY=()
Expand Down Expand Up @@ -583,6 +594,7 @@ end

function _swift_parent_using_command
set -gx SAP_SHELL fish
set -gx SAP_SHELL_VERSION "$FISH_VERSION"
set -l currentCommands (_swift_parent_preprocessor (commandline -opc))
set -l expectedCommands (string split " " $argv[1])
set -l subcommands (string split " " $argv[2])
Expand Down