Summary: Run cmdargs-browser hlint
and you can fill out arguments easily.
The Haskell command line parsing library cmdargs
contains a data type that represents a command line. I always thought it would be a neat trick to transform that into a web page, to make it easier to explore command line options interactively - similar to how the custom-written wget::gui wraps wget
.
I wrote a demo to do just that, named cmdargs-browser
. Given any program that uses cmdargs
(e.g. hlint
), you can install cmdargs-browser
(with cabal install cmdargs-browser
) and run:
cmdargs-browser hlint
And it will pop up:
As we can see, the HLint modes are listed on the left (you can use lint
, grep
or test
), the possible options on the right (e.g. normal arguments and --color
) and the command line it produces at the bottom. As you change mode or add/remove flags, the command line updates. If you hit OK
it then runs the program with the command line. The help is included next to the argument, and if you make a mistake (e.g. write foo
for the --color
flag) it tells you immediately. It could be more polished (e.g. browse buttons for file selections, better styling) but the basic concepts works well.
Technical implementation
I wanted every cmdargs
-using program to support this automatic UI, but also didn't want to increase the dependency footprint or compile-time overhead for cmdargs
. I didn't want to tie cmdargs
to this particular approach to a UI - I wanted a flexible mechanism that anyone could use for other purposes.
To that end, I built out a Helper
module that is included in cmdargs
. That API provides the full power and capabilities on which cmdargs-browser
is written. The Helper
module is only 350 lines.
If you run cmdargs
with either $CMDARGS_HELPER
or $CMDARGS_HELPER_HLINT
set (in the case of HLint) then cmdargs
will run the command line you specify, passing over the explicit Mode
data type on the stdin. That Mode
data type includes functions, and using a simplistic communication channel on the stdin/stdout, the helper process can invoke those functions. As an example, when cmdargs-browser
wants to validate the --color
flag, it does so by calling a function in Mode
, that secretly talks back to hlint
to validate it.
At the end, the helper program can choose to either give an error message (to stop the program, e.g. if you press Cancel), or give some command lines to use to run the program.
Future plans
This demo was a cool project, which may turn out to be useful for some, but I have no intention to develop it further. I think something along these lines should be universally available for all command line tools, and built into all command line parsing libraries.
Historical context
All the code that makes this approach work was written over seven years ago. Specifically, it was my hacking project in the hospital while waiting for my son to be born. Having a little baby is a hectic time of life, so I never got round to telling anyone about its existence.
This weekend I resurrected the code and published an updated version to Hackage, deliberately making as few changes as possible. The three necessary changes were:
- jQuery deprecated the
live
function replacing it withon
, meaning the code didn't work. - I had originally put an upper bound of
0.4
for thetransformers
library. Deleting the upper bound made it work. - Hackage now requires that all your uploaded
.cabal
files declare that they require a version of 1.10 or above of Cabal itself, even if they don't.
Overall, to recover a project that is over 7 years old, it was surprisingly little effort.
Stratus VOS, in the mid 80s, had something similar on ansi-style terminals. The cli arg parser was data-driven, so the api could trivially render it as a form: https://stratadoc.stratus.com/vos/19.0.0/r098-18/cr098-18s.html
ReplyDeleteNice to see the idea pop up again, it's an obvious good thing.
This is cool!
ReplyDeleteI'm intrigued by the "always" next to the --color argument in the screenshot. Are the settings for a given command persistent across calls to `cmdargs-browser`? For instance, if I set `--color=always` the first time I use it on `grep`, will I never have to set that flag again (assuming I always want color)?
@Jeff: In this case --color=always is just for this single invocation. By default HLint uses --color=auto (detect based on the terminal), so this is saying for this run, have color even if you detect the terminal won't work with color. So nothing about persistence. Although the whole HELPER mechanism it builds upon was designed so you could do that, e.g. write a command that always prepends some information.
ReplyDelete