File: cli.mld

package info (click to toggle)
cmdliner 2.1.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 704 kB
  • sloc: ml: 7,287; sh: 146; makefile: 108
file content (524 lines) | stat: -rw-r--r-- 19,609 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
{0:cmdline Command line interface}

This manual describes how your tool ends up interacting
with shells when you use Cmdliner.

{1:invocation Tool invocation}

For tools evaluating a command without subcommands the most general
form of invocation is:

{v
tool [OPTION]… [ARG]…
v}

The tool automatically reponds to the [--help] option by printing
{{!help}the help}. If a version string is provided in the
{{!Cmdliner.Cmd.val-info}command information}, it also automatically
responds to the [--version] option by printing this string on standard
output.

Command line arguments are either {{!optargs}{e optional}} or
{{!posargs}{e positional}}. Both can be freely interleaved but since
[Cmdliner] accepts many optional forms this may result in
ambiguities. The special {{!posargs} token [--]} can be used to
resolve them: anything that follows it is treated as a positional
argument.

Tools evaluating commands with subcommands have this form of invocation

{v
tool [COMMAND]… [OPTION]… [ARG]…
v}

Commands automatically respond to the [--help] option by printing
{{!help}their help}. The sequence of [COMMAND] strings must be the first
strings following the tool name – as soon as an optional argument is
seen the search for a subcommand stops.

{1:args Arguments}

{2:optargs Optional arguments}

An optional argument is specified on the command line by a {e name}
possibly followed by a {e value}.

The name of an option can be short or long.

{ul
{- A {e short} name is a dash followed by a single alphanumeric
   character: [-h], [-q], [-I].}
{- A {e long} name is two dashes followed by alphanumeric
   characters and dashes: [--help], [--silent], [--ignore-case].}}

More than one name may refer to the same optional argument.  For
example in a given program the names [-q], [--quiet] and [--silent]
may all stand for the same boolean argument indicating the program to
be quiet.

The value of an option can be specified in three different ways.

{ul
{- As the next token on the command line: [-o a.out], [--output a.out].}
{- Glued to a short name: [-oa.out].}
{- Glued to a long name after an equal character: [--output=a.out].}}

Glued forms are especially useful if the value itself starts with a
dash as is the case for negative numbers, [--min=-10].

An optional argument without a value is either a {e flag} (see
{!Cmdliner.Arg.flag}, {!Cmdliner.Arg.vflag}) or an optional argument with
an optional value (see the [~vopt] argument of {!Cmdliner.Arg.opt}).

Short flags can be grouped together to share a single dash and the
group can end with a short option. For example assuming [-v] and
[-x] are flags and [-f] is a short option:

{ul
{- [-vx] will be parsed as [-v -x].}
{- [-vxfopt] will be parsed as [-v -x -fopt].}
{- [-vxf opt] will be parsed as [-v -x -fopt].}
{- [-fvx] will be parsed as [-f=vx].}}

{2:posargs Positional arguments}

Positional arguments are tokens on the command line that are not
option names and are not the value of an optional argument. They are
numbered from left to right starting with zero.

Since positional arguments may be mistaken as the optional value of an
optional argument or they may need to look like option names, anything
that follows the special token ["--"] on the command line is
considered to be a positional argument:

{v
tool --option -- but --now we -are --all positional --argu=ments
v}

{2:constraints Constraints on option names}

Using the cmdliner library puts the following constraints on your
command line interface:

{ul
{- The option names [--cmdliner] and [--__complete] are reserved by the
   library.}
{- The option name [--help], (and [--version] if you specify a version
   string) is reserved by the library. Using it as a term or option
   name may result in undefined behaviour.}
{- Defining the same option or command name via two different
   arguments or terms is illegal and raises [Invalid_argument].}}

{1:envlookup Environment variables}

Non-required command line arguments can be backed up by an environment
variable.  If the argument is absent from the command line and
the environment variable is defined, its value is parsed using the
argument converter and defines the value of the argument.

For {!Cmdliner.Arg.flag} and {!Cmdliner.Arg.flag_all} that do not have an
argument converter a boolean is parsed from the lowercased variable value
as follows:

{ul
{- [""], ["false"], ["no"], ["n"] or ["0"] is [false].}
{- ["true"], ["yes"], ["y"] or ["1"] is [true].}
{- Any other string is an error.}}

Note that environment variables are not supported for
{!Cmdliner.Arg.vflag} and {!Cmdliner.Arg.vflag_all}.

{1:help Help and man pages}

Help and man pages are are generated when you call your tool or a subcommand
with [--help]. By default, if the [TERM] environment variable
is not [dumb] or unset, the tool tries to {{!paging}page} the manual
so that you can directly search it. Otherwise it outputs the manual
as plain text.

Alternative help formats can be specified with the optional argument
of [--help], see your own [tool --help] for more information.

{@sh[
tool --help
tool cmd --help
tool --help=groff > tool.1
]}

{2:paging Paging}

The pager is selected by looking up, in order:

{ol
{- The [MANPAGER] variable.}
{- The [PAGER] variable.}
{- The tool [less].}
{- The tool [more].}}

Regardless of the pager, it is invoked with [LESS=FRX] set in the
environment unless, the [LESS] environment variable is set in your
environment.

{2:install_tool_manpages Install}

The manpages of a tool and its subcommands can be installed to a root
[man] directory [$MANDIR] by invoking:

{@shell[
cmdliner install tool-manpages thetool $MANDIR
]}

This looks up [thetool] in the [PATH]. Use an explicit file path like
[./thetool] to directly specify an executable.

If you are also {{!install_tool_completion}installing completions}
rather use the [install tool-support] command, see this
{{!page-cookbook.tip_tool_support}cookbook tip} which also has
instructions on how to install if you are using [opam].

{1:cli_completion Command line completion}

Cmdliner programs automatically get support for shell command line
completion.

The completion process happens via a {{!completion_protocol}protocol}
which is interpreted by generic shell completion scripts that are
installed by the library. For now the [zsh], [bash], and PowerShell
([pwsh]) shells are supported.

Tool developers can easily {{!install_tool_completion}install}
completion definitions that invoke these completion scripts. Tool
end-users need to {{!user_configuration}make sure} these definitions are
looked up by their shell.

{2:user_configuration End-user configuration}

If you are the user of a cmdliner based tool, the following
shell-dependent steps need to be performed in order to benefit from
command line completion.

{3:user_zsh For [zsh]}

The [FPATH] environment variable must be setup to include the
directory where the generic cmdliner completion function is
{{!install_completion} installed} {b before} properly initializing the
completion system.

For example, {{:https://github.com/ocaml/opam/issues/6427}for now}, if
you are using [opam]. You should add something like this to your
[.zshrc]:

{@sh[
FPATH="$(opam var share)/zsh/site-functions:${FPATH}"
autoload -Uz compinit
compinit -u
]}

Also make sure this {b happens before} [opam]'s [zsh] init script
inclusion, see {{:https://github.com/ocaml/opam/issues/6428}this
issue}. Note that these instruction do not react dynamically
to [opam] switches changes so you may see odd completion behaviours
when you do so, see this {{:https://github.com/ocaml/opam/issues/6427}this
opam issue}.

After this, to test everything is right, check that the [_cmdliner_generic]
function can be looked by invoking it (this will result in an error).

{@sh[
> autoload _cmdliner_generic
> _cmdliner_generic
_cmdliner_generic:1: words: assignment to invalid subscript range
]}

If the function cannnot be found make sure the [cmdliner] library is
installed, that the generic scripts were
{{!install_generic_completion}installed} and that the
[_cmdliner_generic] file can be found in one of the directories
mentioned in the [FPATH] variable.

With this setup, if you are using a cmdliner based tool named
[thetool] that did not {{!install_tool_completion}install} a completion
definition. You can always do it yourself by invoking:

{@sh[
autoload _cmdliner_generic
compdef _cmdliner_generic thetool
]}

{3:user_bash For [bash]}

These instructions assume that you have
{{:https://repology.org/project/bash-completion/versions}[bash-completion]}
installed and setup in some way in your [.bashrc].

The [XDG_DATA_DIRS] environment variable must be setup to include the
[share] directory where the generic cmdliner completion function is
{{!install_completion}installed}.

For example, {{:https://github.com/ocaml/opam/issues/6427}for now}, if
you are using [opam]. You should add something like this to your
[.bashrc]:
{@sh[
XDG_DATA_DIRS="$(opam var share):${XDG_DATA_DIRS}"
]}

Note that these instruction do not react dynamically to [opam]
switches changes so you may see odd completion behaviours when you do
so, see this {{:https://github.com/ocaml/opam/issues/6427}this opam
issue}.

After this, to test everything is right, check that the [_cmdliner_generic]
function can be looked up:

{@sh[
> _completion_loader _cmdliner_generic
> declare -F _cmdliner_generic &>/dev/null && echo "Found" || echo "Not found"
Found!
]}

If the function cannot be found make sure the [cmdliner] library is
installed, that the generic scripts were
{{!install_generic_completion}installed} and that the
[_cmdliner_generic] file can be looked up by [_completion_loader].

With this setup, if you are using a cmdliner based tool named
[thetool] that did not {{!install_tool_completion}install} a completion
definition. You can always do it yourself by invoking:

{@sh[
_completion_loader _cmdliner_generic
complete -F _cmdliner_generic thetool
]}

{b Note.} {{:https://github.com/scop/bash-completion/commit/9efc596735c4509001178f0cf28e02f66d1f7703}It seems} [_completion_loader] was deprecated in
bash-completion [2.12] in favour of [_comp_load] but many distributions
are on [< 2.12] and in [2.12] [_completion_loader] simply calls
[_comp_load].

{3:user_pwsh For PowerShell ([pwsh])}

PowerShell Core supports tab completion but lacks the kind of
discovery mechanism that other shells have for these scripts.

Therefore, you need to explicitly source the completion script
for cmdliner based tools. This can be done in the
{{:https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_profiles} [$profile]}
file by adding something like the following:

{@ps1[
# first, load the generic cmdliner completion function
. "$(opam var share)/powershell/cmdliner_generic_completion.ps1"
# then, register each tool you want completions for
# for example:
. "$(opam var share)/powershell/cmdliner_completion.ps1"
. "$(opam var share)/powershell/odoc_completion.ps1"
# ...
]}

Note that these instruction do not react dynamically to [opam]
switches changes so you may see odd completion behaviours when you do
so, see this {{:https://github.com/ocaml/opam/issues/6427}this opam
issue}.

With this setup, if you are using a cmdliner based tool named
[thetool] that did not {{!install_tool_completion}install} a completion
definition. You can always do it yourself by invoking:

{@ps1[
Invoke-Expression $(cmdliner tool-completion pwsh thetool)
]}

or if you find that easier to remember:

{@ps1[
Register-ArgumentCompleter -Native -CommandName thetool -ScriptBlock $Global:_cmdliner_generic
]}

{2:install_completion Install}

Completion scripts need to be installed in subdirectories of a
{{:https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch04s11.html}[share]}
directory which we denote by the [$SHAREDIR] variable below. In a
package installation script this variable is typically defined by:

{@sh[
SHAREDIR="$DESTDIR/$PREFIX/share"
]}

The final destination directory in [share] depends on the shell:

{ul
{- For [zsh] it is [$SHAREDIR/zsh/site-functions]}
{- For [bash] it is [$SHAREDIR/bash-completion/completions]}
{- For [pwsh] it is [$SHAREDIR/powershell]}}

If that is unsatisfying you can output the completion scripts directly
where you want with the [cmdliner generic-completion] and
[cmdliner tool-completion] commands.

{3:install_generic_completion Generic completion scripts}

The generic completion scripts must be installed by the
[cmdliner] library. They should not be part of your tool install.  If
they are not installed you can inspect and install them with the
following invocations, invoke with [--help] for more information.

{@sh[
cmdliner generic-completion zsh   # Output generic zsh script on stdout
cmdliner install generic-completion $SHAREDIR             # All shells
cmdliner install generic-completion --shell zsh $SHAREDIR # Only zsh
]}

Directories are created as needed. Use option [--dry-run] to see which
paths would be written by an [install] invocation.

{3:install_tool_completion Tool completion scripts}

If your tool named [thetool] uses Cmdliner you should install completion
definitions for them. They rely on the {{!install_generic_completion}generic
scripts} to be installed. These tool specific scripts can be inspected
and installed via these invocations:

{@sh[
cmdliner tool-completion zsh thetool  # Output tool zsh script on stdout.
cmdliner install tool-completion thetool $SHAREDIR             # All shells
cmdliner install tool-completion --shell zsh thetool $SHAREDIR # Only zsh
]}

Directories are created as needed. Use option [--dry-run] to see which
paths would be written by an [install] invocation.

If you are also {{!install_tool_manpages}installing manpages} rather
use the [install tool-support] command, see this
{{!page-cookbook.tip_tool_support}cookbook tip} which also has
instructions on how to install if you are using [opam].

{2:completion_protocol Completion protocol}

There is no standard that allows tools and shells to interact to
perform shell command line completion. Completion is supposed to
happen through idiosyncratic, ad-hoc, obscure and brain damaging
shell-specific completion scripts.

To alleviate this, Cmdliner defines one generic script per shell and
interacts with it using the protocol described below. The protocol can
be used to implement generic completion scripts for other shells. The
protocol is versioned but can change even between minor versions of
Cmdliner. Generic scripts for popular shells can be inspected via
the [cmdliner generic-completion] command.

The protocol betwen the shell completion {e script} and a
cmdliner based {e tool} is as follows:

{ol
{- When completion is requested the script invokes the tool with a
   modified command line:
   {ul
   {- The first argument to the tool ([Sys.argv.(1)]) must be the
       option [--__complete].}
   {- The (possibly empty) argument [ARG] on which the completion is
      requested must be replaced by {e exactly} [--__complete=ARG]. Note
      that this can happen after the [--] token, this is the reason
      why we have an explicit [--__complete] argument in [Sys.argv.(1)]:
      it indicates the command line parser must operate in a special mode.}}}
{- The tool responds by writing on standard output a list of
   completion directives which match the [completions] rule of the grammar
   given below.}
{- The script interprets the completion directives according
   to the given semantics below so that the shell can display the
   completions. The script is free to ignore directives
   or data that it is unable to present.}}

The following ABNF grammar is described using the notations of
{{:https://www.rfc-editor.org/rfc/rfc5234}RFC 5234} and
{{:https://www.rfc-editor.org/rfc/rfc7405}RFC 7405}. A few constraints
are not expressed by the grammar:

{ul
{- Except in the [completion] rule, the byte stream may contain ANSI escape
   sequences introduced by the byte [0x1B].}
{- After stripping the ANSI escape sequences, the resulting byte stream must
   be valid UTF-8 text.}}

{@abnf[
completions = version nl directives
version = "1"
directives = *(directive nl)
directive = message / group / %s"files" / %s"dirs" / %"restart"
message = %s"message" nl text nl %s"message-end"
group = %s"group" nl group_name nl *item
group_name = *pchar
item = %s"item" nl completion nl item_doc nl %s"item-end"
completion = *pchar
item_doc = text
text = *(pchar / nl)
nl = %0A
pchar = %20-%7E / %8A-%FF
]}

The semantics of directives is as follows:

{ul
{- A [message] directive defines a message to be reported to the user.
   It is multi-line ANSI styled text which cannot have a line that is
   exactly made of the text [message-end] as it is used to signal the
   end of the message. Messages should be reported in the order they
   are received.}
{- A [group] directive defines an informational [group_name] followed
   by a possibly empty list of completion items that are part of the
   group. An item provides a [completion] value, this is a string that
   defines what the requested [ARG] value can be replaced with. It is
   followed by an [item_doc], multi-line ANSI styled text which cannot
   have a line that is exactly made of the text [item-end] as it is
   used to signal the end of the item.}
{- A [file] directive indicates that the script should add existing
   files staring with [ARG] to completion values.}
{- A [dir] directive indicates that the script should add existing
   directories starting with [ARG] to completion values.}
{- A [restart] directive indicates that the script should restart
   shell completion as if the command line was starting after the leftmost
   [--] disambiguation token. The directive never gets emited if
   there is no [--] on the command line.}}

You can easily inspect the completions of any cmdliner based tool by
invoking it like the protocol suggests. For example for the [cmdliner]
tool itself:

{@shell[
cmdliner --__complete --__complete=
]}

{1:error_message_styling Error message ANSI styling}

Since Cmdliner 2.0 error messages printed on [stderr] use styled text
with ANSI escapes unless one of the following conditions is met:

{ul
{- The [NO_COLOR] environment variable is set and different
   from the empty string. Yes, even if you have [NO_COLOR=false], that's
   what the particularly dumb {:https://no-color.org} standard says.}
{- The [TERM] environment variable is [dumb].}
{- The [TERM] environment variable is unset and {!Sys.backend_type} is
   not [Other "js_of_ocaml"]. Yes, browser consoles support
   ANSI escapes. Yes, you can run Cmdliner in your browser.}}

{1:legacy_prefix_specification Legacy prefix specification}

Before Cmdliner 2.0, command names, long option names and
{!Cmdliner.Arg.enum} values could be specified by a prefix as long as
the prefix was not ambiguous.

This turned out to be a mistake. It makes the user experience of the
tool unstable as it evolves: former user established shortcuts or
invocations in scripts may be broken by new command, option and
enumerant additions.

Therefore this behaviour was unconditionally removed in Cmdliner
2.0. If you happen to have scripts that rely on it, you can invoke
them with [CMDLINER_LEGACY_PREFIXES=true] set in the environment to
recover the old behaviour. {b However the scripts should be fixed: this
escape hatch will be removed in the future.}

The [CMDLINER_LEGACY_PREFIX=true] escape hatch should not be used for
interactive tool interaction. In particular the behaviour of Cmdliner
completion support under this setting is undefined.