# Grouping subcommands

**URL:** <https://forums.swift.org/t/grouping-subcommands/72219>\
**Category:** ArgumentParser\
**Created:** [June 3, 2024, 5:14am UTC](https://forums.swift.org/t/grouping-subcommands/72219 "2024-06-03T05:14:32Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![Douglas\_Gregor](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/douglas_gregor/32/1362_2.png) [@Douglas\_Gregor](https://forums.swift.org/u/Douglas_Gregor)\
**Post date:** [June 3, 2024, 5:14am UTC](https://forums.swift.org/t/grouping-subcommands/72219/1 "2024-06-03T05:14:32Z")

</div>

Hey all,

In a discussion on [adding another `swift package` subcommand](https://forums.swift.org/t/package-manifest-editing-commands-beyond-se-0301/72085/16), we came across the idea of "grouping" common subcommands under a common heading. This can improve the readability of the generated "help" when there are lots of subcommands.

I implemented support for this in a [pull request](https://github.com/apple/swift-argument-parser/pull/644) that extends `CommandConfiguration` with support for naming groups of subcommands along with other commands. I ended up using result builders, because it felt reasonable. Here's a (partial!) example for `swift package`:

```swift
     public static var configuration = CommandConfiguration(
        commandName: "package",
        _superCommandName: "swift",
        abstract: "Perform operations on Swift packages",
        discussion: "SEE ALSO: swift build, swift run, swift test",
        version: SwiftVersion.current.completeDisplayString) {
            CommandGroup(name: "Manifest editing") {
              AddDependency.self
              AddProduct.self
              AddTarget.self
            )

            Clean.self
            PurgeCache.self

            CommandGroup(name: "Package installation") {
              Install.self
              Uninstall.self
            }
        }

```

The result will be that ungrouped subcommands come first, followed by each of the command groups (in order they are specified), e.g.,

```swift
SUBCOMMANDS:
  clean Delete build artifacts
  purge-cache Purge the global repository cache.
  reset Reset the complete cache/build directory
  update Update package dependencies
  describe Describe the current package
  init Initialize a new package

MANIFEST EDITING SUBCOMMANDS:
  add-dependency Add a package dependency to the manifest
  add-target Add a new target to the manifest

PACKAGE INSTALLATION SUBCOMMANDS:
  experimental-install Offers the ability to install executable products of
                          the current package.
  experimental-uninstall Offers the ability to uninstall executable products
                          previously installed by `swift package
                          experimental-install`.

```

My change itself is backward-compatible, so one can still create custom configurations and pass the flat `subcommands` array directly.

Thoughts?

Doug

---

<div class="post-metadata">

**Author:** ![let\_zeppelin](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/let_zeppelin/32/31541_2.png) [@let\_zeppelin](https://forums.swift.org/u/let_zeppelin)\
**Post date:** [June 3, 2024, 7:13am UTC](https://forums.swift.org/t/grouping-subcommands/72219/2 "2024-06-03T07:13:29Z")

</div>

I like the idea a lot, but I am wondering if the term "Section" would be a better fit. Its a closer match to what [SwiftUI](https://developer.apple.com/documentation/swiftui/section) calls the visual organization of content into separate sections and also leaves the door open to use the term "Group" for other future features that support the sharing of properties and functionality.

---

<div class="post-metadata">

**Author:** ![wadetregaskis](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/wadetregaskis/32/8494_2.png) [@wadetregaskis](https://forums.swift.org/u/wadetregaskis)\
**Post date:** [June 3, 2024, 2:57pm UTC](https://forums.swift.org/t/grouping-subcommands/72219/3 "2024-06-03T14:57:42Z")

</div>

I think this is an excellent and valuable extension.

The only thing that seems imperfect, on first blush at least, is the trailing closure syntax. It works, technically, but it's a bit awkward re. the multiple levels of indentation (and the extra punctuation re. curly braces).

Could `CommandConfiguration` instead take a `CommandGroup...` as its final argument? i.e. variadic parameter.

---

<div class="post-metadata">

**Author:** ![allevato](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/allevato/32/385_2.png) [@allevato](https://forums.swift.org/u/allevato)\
**Post date:** [June 3, 2024, 3:25pm UTC](https://forums.swift.org/t/grouping-subcommands/72219/4 "2024-06-03T15:25:27Z")

</div>

> [@wadetregaskis](#):
>
> Could `CommandConfiguration` instead take a `CommandGroup...` as its final argument? i.e. variadic parameter.

That wouldn't be quite equivalent, because the elements in the top-level result builder can be a mixture of `CommandGroup`s and `ParsableCommand`s. So a variadic argument would have to introduce a common protocol for both types, or introduce an explicit "unnamed/default" command group, and you'd lose the ability to easily conditionalize groups directly in the `CommandConfiguration`—the caller would have to hoist the construction of the list of `CommandGroup`s to a helper, which means then you'd have to lose the variadic syntax (or at least have to provide another overload) because you'd need to take arbitrary arrays vs. just a known-at-compile-time variadic list.

Given the "declarative" nature of this kind of configuration, I think the result builder helps a lot more than it harms here.

---

<div class="post-metadata">

**Author:** ![Douglas\_Gregor](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/douglas_gregor/32/1362_2.png) [@Douglas\_Gregor](https://forums.swift.org/u/Douglas_Gregor)\
**Post date:** [June 3, 2024, 4:23pm UTC](https://forums.swift.org/t/grouping-subcommands/72219/5 "2024-06-03T16:23:30Z")

</div>

> [@allevato](#):
>
> Given the "declarative" nature of this kind of configuration, I think the result builder helps a lot more than it harms here.

Right. It gives some flexibility that isn't available within an array literal (e.g., to use if/else) and reduces ceremony somewhat. I considered the alternative of keeping the same "subcommands" but generalizing the list to take some value of type `any CommandOrGroup` for each element. It could look like this:

```swift
     public static var configuration = CommandConfiguration(
        commandName: "package",
        _superCommandName: "swift",
        abstract: "Perform operations on Swift packages",
        discussion: "SEE ALSO: swift build, swift run, swift test",
        version: SwiftVersion.current.completeDisplayString,
        subcommands: [
            CommandGroup(
              name: "Manifest editing",
              subcommands: [
                AddDependency.self,
                AddProduct.self,
                AddTarget.self
              ]
            ),
            Clean.self,
            PurgeCache.self,
            CommandGroup(
              name: "Package installation",
              subcommands: [
                Install.self,
                Uninstall.self
              ]
            )
         )

```

The result builder felt cleaner to me than the nested array literals, but either could work. It's also a little weird that the `subcommands` argument would have a different type from the `subcommands` property, because the former would be generalized (to `[any CommandOrGroup]`) while the latter needs to remain `[any ParsableCommand.Type]` for source-compatibility reasons.

Doug

---

<div class="post-metadata">

**Author:** ![Douglas\_Gregor](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/douglas_gregor/32/1362_2.png) [@Douglas\_Gregor](https://forums.swift.org/u/Douglas_Gregor)\
**Post date:** [June 3, 2024, 4:32pm UTC](https://forums.swift.org/t/grouping-subcommands/72219/6 "2024-06-03T16:32:25Z")

</div>

> [@let\_zeppelin](#):
>
> I like the idea a lot, but I am wondering if the term "Section" would be a better fit. Its a closer match to what [SwiftUI](https://developer.apple.com/documentation/swiftui/section) calls the visual organization of content into separate sections and also leaves the door open to use the term "Group" for other future features that support the sharing of properties and functionality.

Personally, I like the explicitness of calling this `CommandGroup`, and wouldn't expect this little result builder to grow much in generality. It's not the rendering of documentation that we're doing here, it's setting up the subcommand structure.

Doug

---

<div class="post-metadata">

**Author:** ![rauhul](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/rauhul/32/35207_2.png) [@rauhul](https://forums.swift.org/u/rauhul)\
**Post date:** [June 3, 2024, 4:34pm UTC](https://forums.swift.org/t/grouping-subcommands/72219/7 "2024-06-03T16:34:49Z")

</div>

We use the term "Group" in the "OptionGroup" API and I think its best to align with the existing names.

---

<div class="post-metadata">

**Author:** ![Douglas\_Gregor](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/douglas_gregor/32/1362_2.png) [@Douglas\_Gregor](https://forums.swift.org/u/Douglas_Gregor)\
**Post date:** [June 4, 2024, 9:13pm UTC](https://forums.swift.org/t/grouping-subcommands/72219/8 "2024-06-04T21:13:58Z")

</div>

> [@Douglas\_Gregor](#):
>
> The result builder felt cleaner to me than the nested array literals, but either could work. It's also a little weird that the `subcommands` argument would have a different type from the `subcommands` property, because the former would be generalized (to `[any CommandOrGroup]`) while the latter needs to remain `[any ParsableCommand.Type]` for source-compatibility reasons.

Based on more feedback from @nnnnnnnn on the pull request itself, I'm going to revise my suggestion to make it a smaller change to the API. Specifically, adding `groupedSubcommands: [CommandGroup] = []` to the `CommandConfiguration` initializer. So my example would look like this:

```swift
     public static var configuration = CommandConfiguration(
        commandName: "package",
        _superCommandName: "swift",
        abstract: "Perform operations on Swift packages",
        discussion: "SEE ALSO: swift build, swift run, swift test",
        version: SwiftVersion.current.completeDisplayString,
        subcommands: [
            Clean.self,
            PurgeCache.self
        ],
        groupedSubcommands: [
            CommandGroup(
              name: "Manifest editing",
              subcommands: [
                AddDependency.self,
                AddProduct.self,
                AddTarget.self
              ]
            ),
            CommandGroup(
              name: "Package installation",
              subcommands: [
                Install.self,
                Uninstall.self
              ]
            )
         ]
    )

```

Doug
