CLI Person Expertise Case Examine: Topiary
Command line interface (CLI) instruments have a status for being tough
to make use of.
While highly effective as soon as mastered, a CLI inherently lacks a litany of consumer
interface “nice-to-haves”, just by advantage of its medium:
-
Simplicity: Frequent duties must be straightforward to carry out. That is usually
not the case, however worse is when duties might be tweaked in myriad methods,
resulting in a combinatorial explosion of flags, choices and modifiers. -
Memorability: How does one keep in mind all these completely different choices?
It’s not real looking, which is why we so usually resort to double-checking
the utilization textual content,man
pages, or search engine/generative AI du jour. -
Discoverability: Reminding ourselves the way to carry out what we all know
(or assume) might be completed isn’t the identical as guiding us in the direction of the complete
gamut of what’s potential. Even when studying your complete reference
guide (or supply code), this isn’t a positive factor: helpful emergent
behaviour usually goes undocumented. -
Familiarity: So we successfully resort to guessing.
--help
should
give us the utilization textual content, proper? There are frequent idioms which can be
pervasive, however even this isn’t a assure.
- Readability: Open, significant communication is usually a good suggestion
in all walks of life. How usually have you ever run a CLI command to be
introduced with zero output, counting onecho $?
to see if it even
succeeded? Or, on the different excessive, a lot logging noise flooding
your terminal that you may’t inform what’s happening?
tar
, lampooned within the above XKCD, is — at practically 45-years-old — a
product of its age. The present GNU (and, to a lesser extent, BSD) tar
implementations go a way to enhance issues, however outdated habits die onerous
and it’s nonetheless quite common to see tar chzPf
-and-friends within the wild.
Regardless, extra fashionable instruments additionally undergo from related issues; anybody
who has used Git, or shudder GnuPG will attest to this.
As Topiary — Tweag’s code formatting engine — matures, I took the
alternative to modernise its CLI earlier than suboptimal patterns grow to be
entrenched.
In its early improvement, Topiary’s CLI was largely motivated by want.
When options had been added, they had been usually uncovered as command line
arguments, with little thought in regards to the total expertise.
That is what it had organically grown into:
CLI app for Topiary, the common code formatter.
Utilization: topiary [OPTIONS] <--language <LANGUAGE>|--input-files [<INPUT_FILES>...]>
Choices:
-l, --language <LANGUAGE>
Which language to parse and format [possible values: json,
nickel, ocaml, ocaml-interface, ocamllex, toml]
-f, --input-files [<INPUT_FILES>...]
Path to an enter file or a number of enter information. If omitted, or
equal to "-", learn from customary enter. If a number of information are
offered, `in_place` is assumed [default: -]
-q, --query <QUERY>
Which question file to make use of
-o, --output-file <OUTPUT_FILE>
Path to an output file. If omitted, or equal to "-", write to
customary output
-i, --in-place
Format the enter information in place
-v, --visualise[=<OUTPUT_FORMAT>]
Visualise the syntax tree, slightly than format [possible
values: json, dot]
-s, --skip-idempotence
Don't test that formatting twice offers the identical output
--output-configuration
Output the complete configuration to stderr earlier than persevering with
-t, --tolerate-parsing-errors
Format as a lot as potential even when a few of the enter causes
parsing errors
--configuration-override <CONFIGURATION_OVERRIDE>
Override all configuration with the offered file [env:
TOPIARY_CONFIGURATION_OVERRIDE=]
-c, --configuration-file <CONFIGURATION_FILE>
Add the desired configuration file with the best precedence
[env: TOPIARY_CONFIGURATION_FILE=]
-h, --help
Print assist
-V, --version
Print model
Syntax tree visualisation is an efficient instance: This was toggled with the
--visualise
argument, which took an non-obligatory parameter to modify the
output format between GraphViz DOT and JSON. Visualisation was designed
as a unique mode of operation, to assist the event of formatting
queries, however being expressed as an argument may indicate to customers that
formatting ought to nonetheless occur. This implication is bolstered by the
presence of formatting-specific arguments, similar to --skip-idempotence
and --tolerate-parsing-errors
; run along with --visualise
is
meaningless, however it labored regardless.
At a extra elementary degree, there’s the query of I/O: From the place is
enter learn, and to the place is it written? The enter to a formatting software
is sort of vital, to say the least, and if Topiary is to do something
moreover cooking silicon wafers, it’s most likely a good suggestion to do
one thing with its consequence. But an ungainly dance of --input-file
,
--output-file
and --in-place
was imposed. These might appear
self-explanatory — and so they had been, to start with — however issues
change:
-
When assist for formatting a number of inputs was added,
--input-file
turned--input-files
. Nevertheless, what occurs if I attempt to visualise
a number of inputs? -
How do a number of inputs map to outputs? The utilization textual content says that
--in-place
is assumed on this case, however what if I additionally specify an
--output-file
? -
What if I need to work with customary enter or customary output? What
about read-only information? Is--in-place
permitted in these instances? -
Why do I even need to specify
--input-files
(or its quick kind,
-f
)? In fact I’m going to be offering an enter of some form, so
it’s a bit redundant.
Whereas a few of these questions had been answered by the utilization textual content, to essentially
know the behaviour of edge instances, you’d need to experiment or begin
studying the supply code.
Then there are the little issues. Like when offering customized
configuration, how do --configuration-file
and
--configuration-override
work together? (I really did need to learn the
supply code to determine that one out!) What about logging output,
presuming there’s any; how does one entry that?
Loss of life by a thousand cuts.
Don’t get me incorrect: Topiary’s CLI was actually useful on this
state. Nevertheless, it leant too closely on assumptions and the Topiary
workforce’s collective (however inscrutable) data. This made it clumsy,
typically shocking and, total, imposed an excessive amount of friction on new
customers.
So I made a decision to totally rework the CLI. My mandate was clear:
Make unlawful states unrepresentable
It is a basic idiom from strongly typed useful programming,
the place the sort system is leveraged to forbid invalid enter. The identical
logic applies to command line arguments and, as Topiary is written in
Rust, I used to be capable of obtain this utilizing the identical mechanism.
Topiary makes use of the wonderful clap command line argument parser library,
which has a function to derive the parser instantly out of your varieties and
their annotations (similar to doc feedback). This makes the definition of
the CLI arguments purely declarative, leaving you with solely the work of
organising your varieties accurately. All my different CLI modernisation work
stems from right here.
To present some examples of what’s now unrepresentable (i.e., will fail):
- Making an attempt to visualise a number of information;
- Specifying a formatting language when formatting information (which as an alternative
use inference on the filename, pushed by the configuration) or, vice
versa, specifying an enter file when formatting customary enter; - Specifying a formatting question file with no formatting language when
formatting customary enter.
Separate modes of operation
Formatting and visualisation are two distinct modes of operation; they
do various things and take completely different choices. Visualisation with
--in-place
wouldn’t solely be incorrect, it will be catastrophic! The
former CLI additionally had a further “pseudo-mode” of outputting the uncooked
configuration to straightforward error, for debugging functions.
The old style method of separating modes in non-interactive CLI instruments
was to have a number of binaries. This tends to each pollute the worldwide
command namespace (ImageMagick’s convert
, anybody?) and might be hindered
by a scarcity of consistency amongst associated instruments.
The brand new hotness — and by “new”, I imply “for the final decade, or two”
— is to make use of subcommands to mark this separation. In order that’s what I did:
topiary format
codecs your code.topiary visualise
visualises your code.topiary config
outputs the computed configuration, as TOML.
(Formatted, after all!)topiary completion
is a brand new function, which generates shell
completion scripts to assist discoverability.
All have frequent choices and every have particular choices, facilitated by
the kinds I outlined. If a brand new mode is developed sooner or later, it might probably
simply be added with out breaking backwards compatibility.
Make use of acquainted idioms
Interplay with a CLI software, that misses out on the visible cues and
metaphors {that a} GUI can present, must be like a dialogue. (Ideally
a productive and pleasant one, slightly than one which finally ends up with you
questioning your life decisions.) To that finish, a de facto lexicon exists
amongst CLI instruments with frequent behaviours that must be adhered to. It
can be bizarre, for instance, if --assist
was the choice to indicate the
utilization textual content, slightly than --help
.
There are some things I’ve completed within the new Topiary CLI to enhance its
dialog abilities:
-
Whereas
--input-file
and the likes aren’t unprecedented, it’s way more
frequent, when enter is required, to make use of positionalFILE
arguments.
This additionally performs properly with, say, scripts that one might want to write. -
The purpose of a formatter is to format its enter, it due to this fact stands
to motive that--in-place
is implied when coping with information. (A
notable exception to that issed
, however as a “stream editor” first,
it has a bit of extra proper to an--in-place
choice, which isn’t
its default.) -
The power to format customary enter remains to be vital, although. In
which case, slightly than utilizing the-
file conference, information are
merely omitted. This permits Topiary to be put right into a script pipeline
and permits this frequent interplay:$ topiary format --language json ⟵ Invocation {"kind":"your code right here"} ⟵ Normal enter <Ctrl+D> ⟵ EOF { "kind": "your code right here" } ⟵ Formatted customary output
-
Logging info has at all times existed in Topiary, however you wouldn’t
know until you learn theREADME
pretty totally. It was
uncovered by theRUST_LOG
atmosphere variable, which is an
artefact of theenv_logger
library. This has been modified to a
--verbose
flag, following the frequent idiom of “extra occurrences
means extra output” (e.g.,-vvv
maps todebug
logging).
Make frequent duties straightforward and unsurprising
Topiary exposes quite a few choices. These knobs help you change its
behaviour in varied methods and, whereas they shouldn’t be hidden away, they
shouldn’t hinder the “joyful path”. This echoes the design ideas
that had been laid out for Topiary’s formatting kinds: uniform and “good
sufficient”. The identical might be mentioned for the CLI: Simplicity, over steampunk.
Some examples I’ve carried out:
-
Formatting all of your venture’s information is only a matter of operating
topiary format PROJECT_DIR
. Topiary will recursively stroll the
listing (not having to depend on shell growth) and format
each file it understands.See Also -
Visualisation defaults to GraphViz DOT output, slightly than JSON. A PDF
of the syntax tree can now be created with the very pure:topiary visualise /path/to/my.file | dot -Tpdf > syntax-tree.pdf
-
Topiary’s configuration is sourced from a precedence listing after which
collated in one in all 3 ways. This affords a excessive diploma of
customisation, however could make it tough to introspect the runtime
configuration. Nevertheless,topiary config
won’t solely output the
computed configuration — which may then be reused, for
reproducibility’s sake — however Topiary may even annotate it in a method
that allows you to perceive the place it got here from:[[language]] title = "json" extensions = [ "json", ]
Don’t paint your self right into a nook
It’s possible you’ll be considering:
If Topiary is primarily a formatter, why can’t I simply skip the
format
subcommand and have the CLI assume that because the default?
It’s a superb query and one which I requested myself. The most typical job
for the Topiary CLI will likely be formatting, so by my earlier admission of
“making frequent duties straightforward”, certainly this optimisation can be
helpful?
The reply to this has two components:
-
Firstly, clap doesn’t make this straightforward to do, when utilizing its
derivation function. Subcommands can actually be non-obligatory, however to
power the parser right into a “default path or subcommand path” state
machine, whereas sustaining coherent utilization textual content, considerably goes
towards its grain.Shortly after publishing this text, Ed Web page — the principal
contributor of clap — reached out to let me know that clap can
really do that, and fairly straightforwardly. As to my second level,
beneath, whereas typically true, he additionally instructed an fascinating
compromise, which the Topiary workforce will work on
implementing. -
Extra importantly, nevertheless, is that if Topiary commits to offering a
secure interface — which it actually does — then different subcommands
(present and future) are successfully blocked by advantage of the
positional and arbitrary file inputs. What occurs, for instance, if
you need to format a file known asconfig
?
This second level is essential. Not solely does it have the impact of
prohibiting respectable subcommands, however customers will come to depend on this
shorthand which fossilises the interface with suboptimal behaviour.
For a similar motive, I don’t enable subcommand or lengthy choice inference
(that’s, increasing partial CLI arguments when there aren’t any
ambiguities). There are, nevertheless, a handful of subcommand aliases which
are useful to make frequent duties faster to kind (e.g., fmt
for
format
), or when different spellings are frequent.
The complete utilization textual content for what I ended up with is just too lengthy to stick into
this text, however it might probably after all be found in Topiary’s
documentation. Moreover, possibly a bit of demo can be
extra illustrative. Right here’s the way it appears with completion in zsh
(i.e.,
don’t blame Topiary for the clipping):
The trail trod to reach at this vacation spot was not random. It was
knowledgeable by my very own expertise with CLI instruments, guided by the course
wherein clap steers you. Additional polish was added on the recommendation of the
Command Line Interface Guidelines e book. That mentioned, UX analysis
is a respectable self-discipline — involving testing and analytics on, you
know, customers — which is normally the purview of GUIs. I’m not conscious of
any UX analysis completed within the realm of CLIs, however this can be an
fascinating result in comply with.
Within the meantime, the modifications mentioned on this article, in addition to
quite a few “high quality of life” enhancements to the CLI codebase — and different
enhancements to Topiary, as a complete — landed in Topiary
v0.3. Making such sweeping modifications was a giant job that
required cautious planning and evaluate. Nevertheless, I feel the outcomes communicate
for themselves and that this upfront funding will repay as Topiary
blooms.
Because of Nicolas Jeannerod and Erin van der Veen for his or her critiques of
this text.