mirror of
https://github.com/apple/swift-argument-parser.git
synced 2026-06-06 20:18:23 +00:00
433 lines
12 KiB
Swift
433 lines
12 KiB
Swift
//===----------------------------------------------------------*- swift -*-===//
|
|
//
|
|
// This source file is part of the Swift Argument Parser open source project
|
|
//
|
|
// Copyright (c) 2020 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
import XCTest
|
|
import ArgumentParserTestHelpers
|
|
@testable import ArgumentParser
|
|
|
|
final class CompletionScriptTests: XCTestCase {
|
|
}
|
|
|
|
extension CompletionScriptTests {
|
|
struct Path: ExpressibleByArgument {
|
|
var path: String
|
|
|
|
init?(argument: String) {
|
|
self.path = argument
|
|
}
|
|
|
|
static var defaultCompletionKind: CompletionKind {
|
|
.file()
|
|
}
|
|
}
|
|
|
|
enum Kind: String, ExpressibleByArgument, CaseIterable {
|
|
case one, two, three = "custom-three"
|
|
}
|
|
|
|
struct Base: ParsableCommand {
|
|
static var configuration = CommandConfiguration(shouldPromptForMissing: false)
|
|
@Option(help: "The user's name.") var name: String
|
|
@Option() var kind: Kind
|
|
@Option(completion: .list(["1", "2", "3"])) var otherKind: Kind
|
|
|
|
@Option() var path1: Path
|
|
@Option() var path2: Path?
|
|
@Option(completion: .list(["a", "b", "c"])) var path3: Path
|
|
|
|
@Flag(help: .hidden) var verbose = false
|
|
}
|
|
|
|
func testBase_Zsh() throws {
|
|
let script1 = try CompletionsGenerator(command: Base.self, shell: .zsh)
|
|
.generateCompletionScript()
|
|
XCTAssertEqual(zshBaseCompletions, script1)
|
|
|
|
let script2 = try CompletionsGenerator(command: Base.self, shellName: "zsh")
|
|
.generateCompletionScript()
|
|
XCTAssertEqual(zshBaseCompletions, script2)
|
|
|
|
let script3 = Base.completionScript(for: .zsh)
|
|
XCTAssertEqual(zshBaseCompletions, script3)
|
|
}
|
|
|
|
func testBase_Bash() throws {
|
|
let script1 = try CompletionsGenerator(command: Base.self, shell: .bash)
|
|
.generateCompletionScript()
|
|
XCTAssertEqual(bashBaseCompletions, script1)
|
|
|
|
let script2 = try CompletionsGenerator(command: Base.self, shellName: "bash")
|
|
.generateCompletionScript()
|
|
XCTAssertEqual(bashBaseCompletions, script2)
|
|
|
|
let script3 = Base.completionScript(for: .bash)
|
|
XCTAssertEqual(bashBaseCompletions, script3)
|
|
}
|
|
|
|
func testBase_Fish() throws {
|
|
let script1 = try CompletionsGenerator(command: Base.self, shell: .fish)
|
|
.generateCompletionScript()
|
|
XCTAssertEqual(fishBaseCompletions, script1)
|
|
|
|
let script2 = try CompletionsGenerator(command: Base.self, shellName: "fish")
|
|
.generateCompletionScript()
|
|
XCTAssertEqual(fishBaseCompletions, script2)
|
|
|
|
let script3 = Base.completionScript(for: .fish)
|
|
XCTAssertEqual(fishBaseCompletions, script3)
|
|
}
|
|
}
|
|
|
|
extension CompletionScriptTests {
|
|
struct Custom: ParsableCommand {
|
|
static var configuration = CommandConfiguration(shouldPromptForMissing: false)
|
|
@Option(name: .shortAndLong, completion: .custom { _ in ["a", "b", "c"] })
|
|
var one: String
|
|
|
|
@Argument(completion: .custom { _ in ["d", "e", "f"] })
|
|
var two: String
|
|
|
|
@Option(name: .customShort("z"), completion: .custom { _ in ["x", "y", "z"] })
|
|
var three: String
|
|
}
|
|
|
|
func verifyCustomOutput(
|
|
_ arg: String,
|
|
expectedOutput: String,
|
|
file: StaticString = #file, line: UInt = #line
|
|
) throws {
|
|
do {
|
|
_ = try Custom.parse(["---completion", "--", arg])
|
|
XCTFail("Didn't error as expected", file: (file), line: line)
|
|
} catch let error as CommandError {
|
|
guard case .completionScriptCustomResponse(let output) = error.parserError else {
|
|
throw error
|
|
}
|
|
XCTAssertEqual(expectedOutput, output, file: (file), line: line)
|
|
}
|
|
}
|
|
|
|
func testCustomCompletions() throws {
|
|
try verifyCustomOutput("-o", expectedOutput: "a\nb\nc")
|
|
try verifyCustomOutput("--one", expectedOutput: "a\nb\nc")
|
|
try verifyCustomOutput("two", expectedOutput: "d\ne\nf")
|
|
try verifyCustomOutput("-z", expectedOutput: "x\ny\nz")
|
|
|
|
XCTAssertThrowsError(try verifyCustomOutput("--bad", expectedOutput: ""))
|
|
}
|
|
}
|
|
|
|
extension CompletionScriptTests {
|
|
struct EscapedCommand: ParsableCommand {
|
|
static var configuration = CommandConfiguration(shouldPromptForMissing: false)
|
|
@Option(help: #"Escaped chars: '[]\."#)
|
|
var one: String
|
|
|
|
@Argument(completion: .custom { _ in ["d", "e", "f"] })
|
|
var two: String
|
|
}
|
|
|
|
func testEscaped_Zsh() throws {
|
|
XCTAssertEqual(zshEscapedCompletion, EscapedCommand.completionScript(for: .zsh))
|
|
}
|
|
}
|
|
|
|
private let zshBaseCompletions = """
|
|
#compdef base
|
|
local context state state_descr line
|
|
_base_commandname=$words[1]
|
|
typeset -A opt_args
|
|
|
|
_base() {
|
|
integer ret=1
|
|
local -a args
|
|
args+=(
|
|
'--name[The user'"'"'s name.]:name:'
|
|
'--kind:kind:(one two custom-three)'
|
|
'--other-kind:other-kind:(1 2 3)'
|
|
'--path1:path1:_files'
|
|
'--path2:path2:_files'
|
|
'--path3:path3:(a b c)'
|
|
'(-h --help)'{-h,--help}'[Show help information.]'
|
|
)
|
|
_arguments -w -s -S $args[@] && ret=0
|
|
|
|
return ret
|
|
}
|
|
|
|
|
|
_custom_completion() {
|
|
local completions=("${(@f)$($*)}")
|
|
_describe '' completions
|
|
}
|
|
|
|
_base
|
|
"""
|
|
|
|
private let bashBaseCompletions = """
|
|
#!/bin/bash
|
|
|
|
_base() {
|
|
cur="${COMP_WORDS[COMP_CWORD]}"
|
|
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
|
COMPREPLY=()
|
|
opts="--name --kind --other-kind --path1 --path2 --path3 -h --help"
|
|
if [[ $COMP_CWORD == "1" ]]; then
|
|
COMPREPLY=( $(compgen -W "$opts" -- "$cur") )
|
|
return
|
|
fi
|
|
case $prev in
|
|
--name)
|
|
|
|
return
|
|
;;
|
|
--kind)
|
|
COMPREPLY=( $(compgen -W "one two custom-three" -- "$cur") )
|
|
return
|
|
;;
|
|
--other-kind)
|
|
COMPREPLY=( $(compgen -W "1 2 3" -- "$cur") )
|
|
return
|
|
;;
|
|
--path1)
|
|
COMPREPLY=( $(compgen -f -- "$cur") )
|
|
return
|
|
;;
|
|
--path2)
|
|
COMPREPLY=( $(compgen -f -- "$cur") )
|
|
return
|
|
;;
|
|
--path3)
|
|
COMPREPLY=( $(compgen -W "a b c" -- "$cur") )
|
|
return
|
|
;;
|
|
esac
|
|
COMPREPLY=( $(compgen -W "$opts" -- "$cur") )
|
|
}
|
|
|
|
|
|
complete -F _base base
|
|
"""
|
|
|
|
private let zshEscapedCompletion = """
|
|
#compdef escaped-command
|
|
local context state state_descr line
|
|
_escaped_command_commandname=$words[1]
|
|
typeset -A opt_args
|
|
|
|
_escaped-command() {
|
|
integer ret=1
|
|
local -a args
|
|
args+=(
|
|
'--one[Escaped chars: '"'"'\\[\\]\\\\.]:one:'
|
|
':two:{_custom_completion $_escaped_command_commandname ---completion -- two $words}'
|
|
'(-h --help)'{-h,--help}'[Show help information.]'
|
|
)
|
|
_arguments -w -s -S $args[@] && ret=0
|
|
|
|
return ret
|
|
}
|
|
|
|
|
|
_custom_completion() {
|
|
local completions=("${(@f)$($*)}")
|
|
_describe '' completions
|
|
}
|
|
|
|
_escaped-command
|
|
"""
|
|
|
|
private let fishBaseCompletions = """
|
|
# A function which filters options which starts with "-" from $argv.
|
|
function _swift_base_preprocessor
|
|
set -l results
|
|
for i in (seq (count $argv))
|
|
switch (echo $argv[$i] | string sub -l 1)
|
|
case '-'
|
|
case '*'
|
|
echo $argv[$i]
|
|
end
|
|
end
|
|
end
|
|
|
|
function _swift_base_using_command
|
|
set -l currentCommands (_swift_base_preprocessor (commandline -opc))
|
|
set -l expectedCommands (string split " " $argv[1])
|
|
set -l subcommands (string split " " $argv[2])
|
|
if [ (count $currentCommands) -ge (count $expectedCommands) ]
|
|
for i in (seq (count $expectedCommands))
|
|
if [ $currentCommands[$i] != $expectedCommands[$i] ]
|
|
return 1
|
|
end
|
|
end
|
|
if [ (count $currentCommands) -eq (count $expectedCommands) ]
|
|
return 0
|
|
end
|
|
if [ (count $subcommands) -gt 1 ]
|
|
for i in (seq (count $subcommands))
|
|
if [ $currentCommands[(math (count $expectedCommands) + 1)] = $subcommands[$i] ]
|
|
return 1
|
|
end
|
|
end
|
|
end
|
|
return 0
|
|
end
|
|
return 1
|
|
end
|
|
|
|
complete -c base -n '_swift_base_using_command \"base\"' -l name -d 'The user\\\'s name.'
|
|
complete -c base -n '_swift_base_using_command \"base\"' -l kind -r -f -k -a 'one two custom-three'
|
|
complete -c base -n '_swift_base_using_command \"base\"' -l other-kind -r -f -k -a '1 2 3'
|
|
complete -c base -n '_swift_base_using_command \"base\"' -l path1 -r -f -a '(for i in *.{}; echo $i;end)'
|
|
complete -c base -n '_swift_base_using_command \"base\"' -l path2 -r -f -a '(for i in *.{}; echo $i;end)'
|
|
complete -c base -n '_swift_base_using_command \"base\"' -l path3 -r -f -k -a 'a b c'
|
|
complete -c base -n '_swift_base_using_command \"base\"' -s h -l help -d 'Show help information.'
|
|
"""
|
|
|
|
// MARK: - Test Hidden Subcommand
|
|
struct Parent: ParsableCommand {
|
|
static var configuration = CommandConfiguration(
|
|
shouldPromptForMissing: false,
|
|
subcommands: [HiddenChild.self])
|
|
}
|
|
|
|
struct HiddenChild: ParsableCommand {
|
|
static var configuration = CommandConfiguration(
|
|
shouldDisplay: false, shouldPromptForMissing: false)
|
|
}
|
|
|
|
extension CompletionScriptTests {
|
|
func testHiddenSubcommand_Zsh() throws {
|
|
let script1 = try CompletionsGenerator(command: Parent.self, shell: .zsh)
|
|
.generateCompletionScript()
|
|
XCTAssertEqual(zshHiddenCompletion, script1)
|
|
|
|
let script2 = try CompletionsGenerator(command: Parent.self, shellName: "zsh")
|
|
.generateCompletionScript()
|
|
XCTAssertEqual(zshHiddenCompletion, script2)
|
|
|
|
let script3 = Parent.completionScript(for: .zsh)
|
|
XCTAssertEqual(zshHiddenCompletion, script3)
|
|
}
|
|
|
|
func testHiddenSubcommand_Bash() throws {
|
|
let script1 = try CompletionsGenerator(command: Parent.self, shell: .bash)
|
|
.generateCompletionScript()
|
|
XCTAssertEqual(bashHiddenCompletion, script1)
|
|
|
|
let script2 = try CompletionsGenerator(command: Parent.self, shellName: "bash")
|
|
.generateCompletionScript()
|
|
XCTAssertEqual(bashHiddenCompletion, script2)
|
|
|
|
let script3 = Parent.completionScript(for: .bash)
|
|
XCTAssertEqual(bashHiddenCompletion, script3)
|
|
}
|
|
|
|
func testHiddenSubcommand_Fish() throws {
|
|
let script1 = try CompletionsGenerator(command: Parent.self, shell: .fish)
|
|
.generateCompletionScript()
|
|
XCTAssertEqual(fishHiddenCompletion, script1)
|
|
|
|
let script2 = try CompletionsGenerator(command: Parent.self, shellName: "fish")
|
|
.generateCompletionScript()
|
|
XCTAssertEqual(fishHiddenCompletion, script2)
|
|
|
|
let script3 = Parent.completionScript(for: .fish)
|
|
XCTAssertEqual(fishHiddenCompletion, script3)
|
|
}
|
|
}
|
|
|
|
let zshHiddenCompletion = """
|
|
#compdef parent
|
|
local context state state_descr line
|
|
_parent_commandname=$words[1]
|
|
typeset -A opt_args
|
|
|
|
_parent() {
|
|
integer ret=1
|
|
local -a args
|
|
args+=(
|
|
'(-h --help)'{-h,--help}'[Show help information.]'
|
|
)
|
|
_arguments -w -s -S $args[@] && ret=0
|
|
|
|
return ret
|
|
}
|
|
|
|
|
|
_custom_completion() {
|
|
local completions=("${(@f)$($*)}")
|
|
_describe '' completions
|
|
}
|
|
|
|
_parent
|
|
"""
|
|
|
|
let bashHiddenCompletion = """
|
|
#!/bin/bash
|
|
|
|
_parent() {
|
|
cur="${COMP_WORDS[COMP_CWORD]}"
|
|
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
|
COMPREPLY=()
|
|
opts="-h --help"
|
|
if [[ $COMP_CWORD == "1" ]]; then
|
|
COMPREPLY=( $(compgen -W "$opts" -- "$cur") )
|
|
return
|
|
fi
|
|
COMPREPLY=( $(compgen -W "$opts" -- "$cur") )
|
|
}
|
|
|
|
|
|
complete -F _parent parent
|
|
"""
|
|
|
|
let fishHiddenCompletion = """
|
|
# A function which filters options which starts with "-" from $argv.
|
|
function _swift_parent_preprocessor
|
|
set -l results
|
|
for i in (seq (count $argv))
|
|
switch (echo $argv[$i] | string sub -l 1)
|
|
case '-'
|
|
case '*'
|
|
echo $argv[$i]
|
|
end
|
|
end
|
|
end
|
|
|
|
function _swift_parent_using_command
|
|
set -l currentCommands (_swift_parent_preprocessor (commandline -opc))
|
|
set -l expectedCommands (string split " " $argv[1])
|
|
set -l subcommands (string split " " $argv[2])
|
|
if [ (count $currentCommands) -ge (count $expectedCommands) ]
|
|
for i in (seq (count $expectedCommands))
|
|
if [ $currentCommands[$i] != $expectedCommands[$i] ]
|
|
return 1
|
|
end
|
|
end
|
|
if [ (count $currentCommands) -eq (count $expectedCommands) ]
|
|
return 0
|
|
end
|
|
if [ (count $subcommands) -gt 1 ]
|
|
for i in (seq (count $subcommands))
|
|
if [ $currentCommands[(math (count $expectedCommands) + 1)] = $subcommands[$i] ]
|
|
return 1
|
|
end
|
|
end
|
|
end
|
|
return 0
|
|
end
|
|
return 1
|
|
end
|
|
|
|
complete -c parent -n '_swift_parent_using_command \"parent\"' -s h -l help -d 'Show help information.'
|
|
"""
|