mirror of
https://github.com/apple/swift-argument-parser.git
synced 2026-06-06 20:18:23 +00:00
135 lines
4.0 KiB
Swift
135 lines
4.0 KiB
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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
struct HelpCommand: ParsableCommand {
|
|
static let configuration = CommandConfiguration(
|
|
commandName: "help",
|
|
abstract:
|
|
"Show subcommand help information. Use --search to find commands and options.",
|
|
helpNames: [])
|
|
|
|
/// Any subcommand names provided after the `help` subcommand.
|
|
@Argument var subcommands: [String] = []
|
|
|
|
/// Capture and ignore any extra help flags given by the user.
|
|
@Flag(
|
|
name: [.short, .long, .customLong("help", withSingleDash: true)],
|
|
help: .private)
|
|
var help = false
|
|
|
|
/// Search term for finding commands and options.
|
|
@Option(
|
|
name: [.short, .long],
|
|
help: "Search for commands and options matching the term.")
|
|
var search: String?
|
|
|
|
private(set) var commandStack: [ParsableCommand.Type] = []
|
|
private(set) var visibility: ArgumentVisibility = .default
|
|
private(set) var commandTree: Tree<ParsableCommand.Type>?
|
|
|
|
init() {}
|
|
|
|
mutating func run() throws {
|
|
// If search term is provided, perform search instead of showing help
|
|
if let searchTerm = search {
|
|
performSearch(term: searchTerm)
|
|
return
|
|
}
|
|
|
|
throw CommandError(
|
|
commandStack: commandStack,
|
|
parserError: .helpRequested(visibility: visibility))
|
|
}
|
|
|
|
mutating func buildCommandStack(with parser: CommandParser) throws {
|
|
commandStack = parser.commandStack(for: subcommands)
|
|
commandTree = parser.commandTree
|
|
|
|
// If subcommands were specified, find the corresponding node in the tree
|
|
if !subcommands.isEmpty {
|
|
var currentNode = parser.commandTree
|
|
for subcommand in subcommands {
|
|
if let child = currentNode.firstChild(withName: subcommand) {
|
|
currentNode = child
|
|
commandTree = currentNode
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private func performSearch(term: String) {
|
|
guard let tree = commandTree else {
|
|
print("Error: Command tree not initialized.")
|
|
return
|
|
}
|
|
|
|
// Get the tool name for display
|
|
var toolName = commandStack.map { $0._commandName }.joined(separator: " ")
|
|
if toolName.isEmpty {
|
|
toolName = tree.element._commandName
|
|
}
|
|
if let root = commandStack.first,
|
|
let superName = root.configuration._superCommandName
|
|
{
|
|
toolName = "\(superName) \(toolName)"
|
|
}
|
|
|
|
// Create search engine and perform search
|
|
let commandSearcher = CommandSearcher(
|
|
rootNode: tree,
|
|
commandStack: commandStack.isEmpty ? [tree.element] : commandStack,
|
|
visibility: visibility
|
|
)
|
|
|
|
let results = commandSearcher.search(for: term)
|
|
|
|
// Format and print results
|
|
let output = CommandSearcher.formatResults(
|
|
results,
|
|
term: term,
|
|
toolName: toolName,
|
|
screenWidth: HelpGenerator.systemScreenWidth
|
|
)
|
|
|
|
print(output)
|
|
}
|
|
|
|
/// Used for testing.
|
|
func generateHelp(screenWidth: Int) -> String {
|
|
HelpGenerator(
|
|
commandStack: commandStack,
|
|
visibility: visibility
|
|
)
|
|
.rendered(screenWidth: screenWidth)
|
|
}
|
|
|
|
enum CodingKeys: CodingKey {
|
|
case subcommands
|
|
case help
|
|
case search
|
|
}
|
|
|
|
init(from decoder: Decoder) throws {
|
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
self.subcommands = try container.decode([String].self, forKey: .subcommands)
|
|
self.help = try container.decode(Bool.self, forKey: .help)
|
|
self.search = try container.decodeIfPresent(String.self, forKey: .search)
|
|
}
|
|
|
|
init(commandStack: [ParsableCommand.Type], visibility: ArgumentVisibility) {
|
|
self.commandStack = commandStack
|
|
self.visibility = visibility
|
|
self.subcommands = commandStack.map { $0._commandName }
|
|
self.help = false
|
|
self.search = nil
|
|
}
|
|
}
|