Hello everyone,
I’d like to start a thread about how to improve Swift’s user experience from the command line across all platforms.
Specifically, I’d like to make the command line interface more appropriate for direct, interactive use by humans, while keeping automation use cases available. I propose to rework the CL tools in the following ways:
- Give users direction in accomplishing their Swift tasks at the command line, providing choices for high level tasks and clarification about what to do next where possible.
- Progressively disclose flags and options, keeping text focused on the current task.
- Make the command line aware of context so it can infer the right action for impactful cases.
- Continue to provide explicit invocations for all of the tasks we perform today.
Motivation
The Swift CLI has grown organically over time to include a large number of flags and options for many individual needs. In fact, you could argue the CLI was never truly “designed” to scale to the features it offers today. In order to use it successfully, you need to know which combination of options go together for a given task, many of which are designed to be driven by an IDE or build system, not a human. In fact, typing swift --help
presents around 150 lines of options, with --help-hidden
increasing the count to around 250. The tool would greatly benefit from a true UX design.
The command line is also currently inconsistent with how it presents options, for example:
- Typing
swift --help
implies compilation with the driver and presents many build-related options, but also presents other unrelated subcommands. - With no arguments,
swift
invokes the REPL, but this isn’t particularly obvious. - The
swift build
command is for packages, but we also elevate raw code compilation with theswiftc
command.
As a top goal of a redesign, the command line should always presents clear, consistent, and intuitive avenues through high level tasks. The use of sub-commands will make grouping of tasks more logical and easier to discover. This approach will also make it easier to add new functionality while keeping the commands organized, and more approachable for developers of all experience levels.
Once the top-level commands are better organized, I’d like to add features that further elevate the value of sample code and documentation, too. We can make the CL experience easier for newcomers learning the language, and for anyone investigating a new package or code base to learn it by example.
Design Sketch
The proposed idea, broken down into a few sub-goals:
1. Establish a concise, colorful look and feel.
To improve readability and differentiation of descriptions, lists, menus, or code listings printed to the terminal, I’d like to adopt terminal colors and establish a concise and spacious look and feel to the text. To give an example of a project that has some inspiring similarities, check out the GitHub CLI: https://cli.github.com.
2. Make the swift
command act as a high-level router to subcommands.
With the arrival of the Swift Package Manager, we’ve already been moving in this direction. I'd like to continue with an additional focus on subcommands. For example, today the swift
command will assume you mean to invoke the compiler when there isn't a subcommand, although it's not clear that is always the intended outcome, and we have swiftc
for that case, as well.
The change I propose here is to just list the highest level subcommands instead, when there isn’t anything more specific requested. There are two small branches I have ready to get this started which include some improvements to the presentation, as well:
https://github.com/bitjammer/swift-driver/tree/acgarland/help-intro
3. Require the swift repl
command to launch the REPL
Launching the REPL should be consistent with the other swift
subcommands. The tools already recognize swift repl
as a subcommand, and will become much more discoverable when listed among the top subcommands in the new UX. By not launching the REPL by default, room is opened up for other default behaviors based on context (see next section.)
4. Run certain subcommands automatically based on context
When you run a tool like swift
with no parameters, the default behavior is generally chosen to either 1) encourage specific user behavior, or 2) do the thing users most often want. Preferably, something happens that feels like both. Given the user’s context, we can pick default behaviors more likely to be helpful for the user. For instance, if your shell’s current folder is within the root of a package, it may make sense to list all targets within that package if you just type swift
, or provide a menu to explore and run code snippets.
Proposed new behavior: code snippets. The first contextual behavior I’d like to propose is a new way to explore a package. Imagine an interactive help experience that surfaces a package's documentation, and can even browse and run a package's bundled code snippets. Every time you download a new Swift package, you can immediately experience how that code is used in focused examples.
I have been prototyping some ideas on snippets specifically, and would love to spend some time to get feedback and go deeper on this soon in a separate pitch. The approach is designed to make it much more rewarding to document packages, and provide example code showing off the API. And if the idea catches on, the Swift community will benefit from a standard way to learn about new packages more easily.
I’ll try to post some code and a further pitch on this idea soon.
Consistency is still very important, so even if we offer contextual behaviors, every option the user may want will also need a specific command to perform that task with 100% reliability.
5. The swiftc
command continues to serve as the way to automate compilation.
The swiftc
tool already exists for direct control of the Swift compiler. Again, for consistency, a command such as swift compile
could be added to the main swift
command, and to complement the swift build
command.
What this end state means is that the swift
command serves as an entry point for people to perform high-level tasks with Swift, and swiftc
is dedicated to compilation with the driver.