Skip to content

Replace the cursor index within the completing word parameter of custom-completion closures with a String completion prefix parameter #770

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Examples/math/Math.swift
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ extension Math.Statistics {
}
}

func customCompletion(_ s: [String], _: Int, _: Int) -> [String] {
func customCompletion(_ s: [String], _: Int, _: String) -> [String] {
(s.last ?? "").starts(with: "a")
? ["aardvark", "aaaaalbert"]
: ["hello", "helicopter", "heliotrope"]
Expand Down
18 changes: 9 additions & 9 deletions Sources/ArgumentParser/Parsable Properties/CompletionKind.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public struct CompletionKind {
case file(extensions: [String])
case directory
case shellCommand(String)
case custom(@Sendable ([String], Int, Int) -> [String])
case custom(@Sendable ([String], Int, String) -> [String])
case customDeprecated(@Sendable ([String]) -> [String])
}

Expand Down Expand Up @@ -126,11 +126,11 @@ public struct CompletionKind {
/// passed to Swift as `"abc\\""def"` (i.e. the Swift String's contents would
/// include all 4 of the double quotes and the 2 consecutive backslashes).
///
/// The first of the two `Int` arguments is the 0-based index of the word
/// for which completions are being requested within the given `[String]`.
/// The second argument (an `Int`) is the 0-based index of the word for which
/// completions are being requested within the given `[String]`.
///
/// The second of the two `Int` arguments is the 0-based index of the shell
/// cursor within the word for which completions are being requested.
/// The third argument (a `String`) is the prefix of the word for which
/// completions are being requested that precedes the cursor.
///
/// ### bash
///
Expand Down Expand Up @@ -171,21 +171,21 @@ public struct CompletionKind {
/// character, not as after the backslash.
@preconcurrency
public static func custom(
_ completion: @Sendable @escaping ([String], Int, Int) -> [String]
_ completion: @Sendable @escaping ([String], Int, String) -> [String]
) -> CompletionKind {
CompletionKind(kind: .custom(completion))
}

/// Deprecated; only kept for backwards compatibility.
///
/// The same as `custom(@Sendable @escaping ([String], Int, Int) -> [String])`,
/// except that index arguments are not supplied.
/// The same as `custom(@Sendable @escaping ([String], Int, String) -> [String])`,
/// except that the last two closure arguments are not supplied.
@preconcurrency
@available(
*,
deprecated,
message:
"Provide a three-parameter closure instead. See custom(@Sendable @escaping ([String], Int, Int) -> [String])."
"Provide a three-parameter closure instead. See custom(@Sendable @escaping ([String], Int, String) -> [String])."
)
public static func custom(
_ completion: @Sendable @escaping ([String]) -> [String]
Expand Down
17 changes: 14 additions & 3 deletions Sources/ArgumentParser/Parsing/CommandParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -456,16 +456,27 @@ extension CommandParser {
}

guard
let s = args.popFirst(),
let cursorIndexWithinCompletingArgument = Int(s)
let arg = args.popFirst(),
let cursorIndexWithinCompletingArgument = Int(arg)
else {
throw ParserError.invalidState
}

let completingPrefix: String
if let completingArgument = args.last {
completingPrefix = String(
completingArgument.prefix(cursorIndexWithinCompletingArgument)
)
} else if cursorIndexWithinCompletingArgument == 0 {
completingPrefix = ""
} else {
throw ParserError.invalidState
}

completions = complete(
Array(args),
completingArgumentIndex,
cursorIndexWithinCompletingArgument
completingPrefix
)
case .customDeprecated(let complete):
completions = complete(args)
Expand Down
4 changes: 2 additions & 2 deletions Sources/ArgumentParserToolInfo/ToolInfo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,9 @@ public struct ArgumentInfoV0: Codable, Hashable {
case directory
/// Call the given shell command to generate completions.
case shellCommand(command: String)
/// Generate completions using the given closure including index arguments.
/// Generate completions using the given three-parameter closure.
case custom
/// Generate completions using the given closure without index arguments.
/// Generate completions using the given one-parameter closure.
@available(*, deprecated, message: "Use custom instead.")
case customDeprecated
}
Expand Down
Loading