This commit is contained in:
c-cube 2025-12-01 22:21:55 +00:00
parent 247371f990
commit e14e9845e6
10 changed files with 196 additions and 46 deletions

View file

@ -1,3 +1,36 @@
v2.1.0 2025-11-25 Zagreb
------------------------
- completion: add support for powershell (#214).
Thanks to Brian Ward for the work.
- bash completion: improve bash completion on files and directories (#238).
Thanks to Brian Ward for the report and fix.
- bash completion: improve completion of long options with `=` (#231).
Thanks to Brian Ward for the patch.
- zsh completion: fix completion of files and directories on glued
forms (#230).
Thanks to Brian Ward for the help.
- zsh completion: strip ANSI escapes from doc strings. The experience
is too unreliable (#220).
- cmdliner tool: add support for generating standalone completion
scripts via the `--standalone-completion` option. Can be used if you
distribute your software in a context where the cmdliner library
may not be installed (#243).
Thanks to Brian Ward for suggesting.
- Add alternative instructions to the cookbook for installing tool support
files with `dune` (#250).
Thanks to Brian Ward for the patch and research.
- `--help` output, improve graceful degradation on groff or pager
errors (#140).
Thanks to Sergey Fedorov for the report.
v2.0.0 2025-09-26 Zagreb
------------------------

View file

@ -183,8 +183,8 @@ 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] and [bash] shells are
supported.
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
@ -295,6 +295,45 @@ 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
@ -310,7 +349,8 @@ 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 [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

View file

@ -96,8 +96,8 @@ If you are porting your command line parsing to [Cmdliner] and that
you have conventions that clash with [Cmdliner]'s ones but you need to
preserve backward compatibility, one way of proceeding is to
pre-process {!Sys.argv} into a new array of the right shape before
giving it to command {{!Cmdliner.Cmd.section-eval}evaluation
functions} via the [?argv] optional argument.
giving it to command {{!Cmdliner.Cmd.section-eval}evaluation functions}
via the [?argv] optional argument.
These are two common cases:
@ -172,7 +172,7 @@ let cmd =
Cmd.group (Cmd.info "tool") ~default @@
[Cmd_import.cmd; Cmd_serve.cmd; Cmd_user.cmd]
let main () = Cmd.value' cmd
let main () = Cmd.eval' cmd
let () = if !Sys.interactive then () else exit (main ())
]}
@ -213,22 +213,70 @@ If [cmdliner] is only an optional dependency of your package use the
opam filter [{cmdliner:installed}] after the closing bracket of the command
invocation.
{3:top_tool_support_with_dune_install With [dune build @install]}
Users of [dune] 3.5 or later can use the [cmdliner install
tool-support] command described above, along with the
{{:https://dune.readthedocs.io/en/stable/reference/dune/rule.html#directory-targets}
[directory-targets]}
feature and
{{:https://dune.readthedocs.io/en/latest/reference/dune/install.html}
[install]} stanza, to hook into the existing [dune build @install] command.
For an executable named [mytool] defined in a [dune] file, you can add
the following to the same [dune] file to populate the completion
scripts and manpages when running [dune build @install] (which is what
is used by [opam install]):
{@dune[
(rule
(target (dir cmdliner-support))
(deps mytool.exe)
(action
(ignore-stdout
(run cmdliner install tool-support ./mytool.exe:mytool cmdliner-support))))
(install
(section share_root)
(dirs (cmdliner-support/share as .)))
]}
If these features of dune are not available to your project, you may
instead need to update the [opam] file, see {{!tip_tool_support_with_opam_dune}
these instructions}.
{3:tip_tool_support_with_opam_dune With [opam] and [dune]}
First make sure your understand the
{{!tip_tool_support_with_opam}above basic instructions} for [opam].
You then
{{:https://dune.readthedocs.io/en/stable/reference/packages.html#generating-opam-files}need to figure out} how to add the [cmdliner install] instruction to the [build:]
field of the opam file after your [dune] build instructions. For a tool named
[tool] the result should eventually look this:
{{:https://dune.readthedocs.io/en/stable/reference/packages.html#generating-opam-files}
need to add a [.opam.template]} file which inserts the [cmdliner
install] instruction to the [build:] field of the opam file after your
[dune] build instructions. For a tool named [mytool] the
[mytool.opam.template] file should contain:
{@sh[
build: [
[ … ] # Your regular dune build instructions
# These first two commands are what dune generates if you don't have
# a template
["dune" "subst"] {dev}
[
"dune"
"build"
"-p"
name
"-j"
jobs
"@install"
"@runtest" {with-test}
"@doc" {with-doc}
]
# This is the important command for cmdliner's files
["cmdliner" "install" "tool-support"
"--update-opam-install=%{_:name}%.install"
"_build/default/install/bin/tool" {os != "win32"}
"_build/default/install/bin/tool.exe" {os = "win32"}
"_build/install/default/bin/mytool" {os-family != "windows"}
"_build/install/default/bin/mytool.exe" {os-family = "windows"}
"_build/cmdliner-install"]]
]}
@ -254,11 +302,11 @@ to support these conventions:
{[
let infile =
let doc = "$(docv) is the file to read from. Use $(b,-) for $(b,stdin)" in
Arg.(value & opt string "-" & info ["i", "input-file"] ~doc ~docv:"FILE")
Arg.(value & opt filepath "-" & info ["i", "input-file"] ~doc ~docv:"FILE")
let outfile =
let doc = "$(docv) is the file to write to. Use $(b,-) for $(b,stdout)" in
Arg.(value & opt string "-" & info ["o", "output-file"] ~doc ~docv:"FILE")
Arg.(value & opt filepath "-" & info ["o", "output-file"] ~doc ~docv:"FILE")
]}
Here is {!Stdlib} based code to read to a string a file or standard
@ -637,7 +685,7 @@ open Cmdliner
open Cmdliner.Term.Syntax
let cmd =
Cmd.make (Cmd.info "TODO" ~version:"v2.0.0") @@
Cmd.make (Cmd.info "TODO" ~version:"v2.1.0") @@
let+ unit = Term.const () in
tool unit
@ -660,7 +708,7 @@ open Cmdliner.Term.Syntax
let flag = Arg.(value & flag & info ["flag"] ~doc:"The flag")
let infile =
let doc = "$(docv) is the input file. Use $(b,-) for $(b,stdin)." in
Arg.(value & pos 0 string "-" & info [] ~doc ~docv:"FILE")
Arg.(value & pos 0 filepath "-" & info [] ~doc ~docv:"FILE")
let cmd =
let doc = "The tool synopsis is TODO" in
@ -672,7 +720,7 @@ let cmd =
Cmd.Exit.info exit_todo ~doc:"When there is stuff todo" ::
Cmd.Exit.defaults
in
Cmd.make (Cmd.info "TODO" ~version:"v2.0.0" ~doc ~man ~exits) @@
Cmd.make (Cmd.info "TODO" ~version:"v2.1.0" ~doc ~man ~exits) @@
let+ flag and+ infile in
tool ~flag ~infile
@ -696,7 +744,7 @@ open Cmdliner.Term.Syntax
let flag = Arg.(value & flag & info ["flag"] ~doc:"The flag")
let infile =
let doc = "$(docv) is the input file. Use $(b,-) for $(b,stdin)." in
Arg.(value & pos 0 file "-" & info [] ~doc ~docv:"FILE")
Arg.(value & pos 0 filepath "-" & info [] ~doc ~docv:"FILE")
let hey_cmd =
let doc = "The hey command synopsis is TODO" in
@ -712,7 +760,7 @@ let ho_cmd =
let cmd =
let doc = "The tool synopsis is TODO" in
Cmd.group (Cmd.info "TODO" ~version:"v2.0.0" ~doc) @@
Cmd.group (Cmd.info "TODO" ~version:"v2.1.0" ~doc) @@
[hey_cmd; ho_cmd]
let main () = Cmd.eval' cmd

View file

@ -81,7 +81,7 @@ let rm_cmd =
`S Manpage.s_bugs; `P "Report bugs to <bugs@example.org>.";
`S Manpage.s_see_also; `P "$(b,rmdir)(1), $(b,unlink)(2)" ]
in
Cmd.make (Cmd.info "rm" ~version:"v2.0.0" ~doc ~man) @@
Cmd.make (Cmd.info "rm" ~version:"v2.1.0" ~doc ~man) @@
let+ prompt and+ recursive and+ files in
rm ~prompt ~recurse:recursive files
@ -154,7 +154,7 @@ let cp_cmd =
`S Manpage.s_bugs;
`P "Email them to <bugs@example.org>."; ]
in
Cmd.make (Cmd.info "cp" ~version:"v2.0.0" ~doc ~man ~man_xrefs) @@
Cmd.make (Cmd.info "cp" ~version:"v2.1.0" ~doc ~man ~man_xrefs) @@
Term.ret @@
let+ verbose and+ recurse and+ force and+ srcs and+ dest in
cp ~verbose ~recurse ~force srcs dest
@ -265,7 +265,7 @@ let tail_cmd =
`S Manpage.s_see_also;
`P "$(b,cat)(1), $(b,head)(1)" ]
in
Cmd.make (Cmd.info "tail" ~version:"v2.0.0" ~doc ~man) @@
Cmd.make (Cmd.info "tail" ~version:"v2.1.0" ~doc ~man) @@
let+ lines and+ follow and+ verb and+ pid and+ files in
tail ~lines ~follow ~verb ~pid files
@ -444,7 +444,7 @@ let help_cmd =
let main_cmd =
let doc = "a revision control system" in
let man = help_secs in
let info = Cmd.info "darcs" ~version:"v2.0.0" ~doc ~sdocs ~man in
let info = Cmd.info "darcs" ~version:"v2.1.0" ~doc ~sdocs ~man in
let default = Term.(ret (const (fun _ -> `Help (`Pager, None)) $ copts_t)) in
Cmd.group info ~default [initialize_cmd; record_cmd; help_cmd]

View file

@ -1,4 +1,4 @@
{0 Cmdliner {%html: <span class="version">v2.0.0</span>%}}
{0 Cmdliner {%html: <span class="version">v2.1.0</span>%}}
Cmdliner provides a simple and compositional mechanism
to convert command line arguments to OCaml values and pass them to

View file

@ -152,7 +152,7 @@ let chorus_cmd =
`S Manpage.s_bugs;
`P "Email bug reports to <bugs@example.org>." ]
in
Cmd.make (Cmd.info "chorus" ~version:"v2.0.0" ~doc ~man) @@
Cmd.make (Cmd.info "chorus" ~version:"v2.1.0" ~doc ~man) @@
let+ count and+ msg in
chorus ~count msg

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -53,7 +53,7 @@ let rm_cmd =
`S Manpage.s_bugs; `P &quot;Report bugs to &lt;bugs@example.org&gt;.&quot;;
`S Manpage.s_see_also; `P &quot;$(b,rmdir)(1), $(b,unlink)(2)&quot; ]
in
Cmd.make (Cmd.info &quot;rm&quot; ~version:&quot;v2.0.0&quot; ~doc ~man) @@
Cmd.make (Cmd.info &quot;rm&quot; ~version:&quot;v2.1.0&quot; ~doc ~man) @@
let+ prompt and+ recursive and+ files in
rm ~prompt ~recurse:recursive files
@ -104,7 +104,7 @@ let cp_cmd =
`S Manpage.s_bugs;
`P &quot;Email them to &lt;bugs@example.org&gt;.&quot;; ]
in
Cmd.make (Cmd.info &quot;cp&quot; ~version:&quot;v2.0.0&quot; ~doc ~man ~man_xrefs) @@
Cmd.make (Cmd.info &quot;cp&quot; ~version:&quot;v2.1.0&quot; ~doc ~man ~man_xrefs) @@
Term.ret @@
let+ verbose and+ recurse and+ force and+ srcs and+ dest in
cp ~verbose ~recurse ~force srcs dest
@ -188,7 +188,7 @@ let tail_cmd =
`S Manpage.s_see_also;
`P &quot;$(b,cat)(1), $(b,head)(1)&quot; ]
in
Cmd.make (Cmd.info &quot;tail&quot; ~version:&quot;v2.0.0&quot; ~doc ~man) @@
Cmd.make (Cmd.info &quot;tail&quot; ~version:&quot;v2.1.0&quot; ~doc ~man) @@
let+ lines and+ follow and+ verb and+ pid and+ files in
tail ~lines ~follow ~verb ~pid files
@ -338,7 +338,7 @@ let help_cmd =
let main_cmd =
let doc = &quot;a revision control system&quot; in
let man = help_secs in
let info = Cmd.info &quot;darcs&quot; ~version:&quot;v2.0.0&quot; ~doc ~sdocs ~man in
let info = Cmd.info &quot;darcs&quot; ~version:&quot;v2.1.0&quot; ~doc ~sdocs ~man in
let default = Term.(ret (const (fun _ -&gt; `Help (`Pager, None)) $ copts_t)) in
Cmd.group info ~default [initialize_cmd; record_cmd; help_cmd]

View file

@ -32,7 +32,7 @@ let count =
`S Manpage.s_bugs;
`P &quot;Email bug reports to &lt;bugs@example.org&gt;.&quot; ]
in
Cmd.make (Cmd.info &quot;chorus&quot; ~version:&quot;v2.0.0&quot; ~doc ~man) @@
Cmd.make (Cmd.info &quot;chorus&quot; ~version:&quot;v2.1.0&quot; ~doc ~man) @@
let+ count and+ msg in
chorus ~count msg