Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ For breaking changes, check [here](#breaking-changes).

[Babashka CLI](https://github.com/babashka/cli): turn Clojure functions into CLIs!

## Unreleased

- [#233](https://github.com/babashka/cli/issues/233): Generated help no longer prints extra spaces when `:prog` is not specified

## 0.12.91

- zsh completion: disable sorting for registered program names to preserve candidate order
Expand Down
20 changes: 13 additions & 7 deletions src/babashka/cli.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -1248,7 +1248,8 @@
(remove (comp :no-doc second) pairs)))

(defn- help-usage-line [prog node any-options?]
(str "Usage: " prog
(str "Usage:"
(when prog (str " " prog))
(when any-options? " [options]")
;; `<command>` reflects the runtime contract (the node accepts/requires
;; a command), so it keys on the dispatchable children, not the
Expand Down Expand Up @@ -1318,12 +1319,12 @@
(conj (str "Inherited options:\n" (format-opts {:spec inherited})))

(seq cmds)
(conj (str "Run \"" prog " <command> --help\" for more information on a command."))
(conj (str "Run \"" (when prog (str prog " ")) "<command> --help\" for more information on a command."))

(seq parents)
(conj (str/join "\n"
(for [{p :prog n :name} parents]
(str "Run \"" p " --help\" for " n " options."))))
(str "Run \"" (when p (str p " ")) "--help\" for " n " options."))))

;; free-text the entry supplies (examples, notes), rendered verbatim
;; last, as a closing footer (argparse epilog / picocli footer convention)
Expand Down Expand Up @@ -1944,6 +1945,10 @@ $env.config.completions.external.completer = {|spans|
(or (str/starts-with? s "-")
(str/starts-with? s ":"))))

(defn- prog-cmd [prog path]
(let [cmd-path (if (seq prog) (cons prog path) path)]
(when (seq cmd-path)
(str/join " " cmd-path))))

(defn- command-help-context
"Given a dispatch `tree`, command path `cmds`, `prog` name and dispatch-level
Expand All @@ -1960,7 +1965,7 @@ $env.config.completions.external.completer = {|spans|
spec may be a vec-of-pairs); display order is handled by `render-help`."
[tree cmds prog inherit global-spec]
(let [node-at (fn [path] (get-in tree (interleave (repeat :cmd) path)))
prog-at (fn [path] (str/join " " (cons prog path)))
prog-at (fn [path] (prog-cmd prog path))
;; options available at the target level itself (e.g. an injected --help,
;; or a redefined option) - those never need a "must precede" pointer
here (set (keys (->spec-map (:spec (node-at cmds)))))
Expand Down Expand Up @@ -2011,7 +2016,7 @@ $env.config.completions.external.completer = {|spans|
* `:table` - a `dispatch` table, or a tree (hand-written or from
[[table->tree]]) - see [[dispatch]] (required)
* `:cmds` - the command path, e.g. `[\"deps\" \"outdated\"]` (default `[]`)
* `:prog` - program name shown in the usage line (required)
* `:prog` - program name shown in the usage line
* `:inherit` - only needed when you pass a dispatch-level `:inherit` to
`dispatch`; pass the same value so `Inherited options:` matches.
Per-option `:inherit true` is detected automatically.
Expand Down Expand Up @@ -2101,8 +2106,9 @@ $env.config.completions.external.completer = {|spans|
(let [tree (table->tree tree)
path (or dispatch [])
ctx-at (fn [p] (command-help-context tree (vec p) prog inherit (::global-spec data)))
hint (str "Run \"" (str/join " " (cons prog path))
" --help\" for more information.")
prog-cmd-path (prog-cmd prog path)
hint (str "Run \"" (when prog-cmd-path (str prog-cmd-path " "))
"--help\" for more information.")
usage (fn [p]
(let [{:keys [node prog inherited]} (ctx-at p)]
(help-usage-line prog node (or (seq (:spec node))
Expand Down
40 changes: 39 additions & 1 deletion test/babashka/cli_test.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -1231,6 +1231,12 @@
"Options:\n -v, --verbose Verbose output\n\n"
"Run \"example <command> --help\" for more information on a command.")
(cli/format-command-help {:table table :prog "example"}))))
(testing "no :prog"
(is (= (str "Usage: [options] <command>\n\n"
"Commands:\n copy Copy a file\n delete Delete a file\n\n"
"Options:\n -v, --verbose Verbose output\n\n"
"Run \"<command> --help\" for more information on a command.")
(cli/format-command-help {:table table}))))
(testing "leaf: own options + option inherited from an ancestor"
(is (= (str "Usage: example copy [options]\n\n"
"Copy a file\n\n"
Expand Down Expand Up @@ -1312,7 +1318,30 @@
(println (cli/format-command-help
{:table tree :cmds dispatch :prog prog :inherit inherit})))})]
(is (str/includes? out "BANNER"))
(is (str/includes? out "Usage: p"))))))
(is (str/includes? out "Usage: p"))))
(testing "multi-word commands with inheritance"
(is (= (str "Usage: myprog foo bar [options]\n\n"
"Options:\n"
" --some-foo-bar-opt\n\n"
"Run \"myprog --help\" for global options.\n"
"Run \"myprog foo --help\" for foo options.")
(cli/format-command-help
{:table [{:cmds [] :spec {:some-default-opt {}}}
{:cmds ["foo"] :spec {:some-foo-opt {}}}
{:cmds ["foo" "bar"] :spec {:some-foo-bar-opt {}}}]
:cmds ["foo" "bar"]
:prog "myprog"})))
(testing "no :prog"
(is (= (str "Usage: foo bar [options]\n\n"
"Options:\n"
" --some-foo-bar-opt\n\n"
"Run \"--help\" for global options.\n"
"Run \"foo --help\" for foo options.")
(cli/format-command-help
{:table [{:cmds [] :spec {:some-default-opt {}}}
{:cmds ["foo"] :spec {:some-foo-opt {}}}
{:cmds ["foo" "bar"] :spec {:some-foo-bar-opt {}}}]
:cmds ["foo" "bar"]})))))))

(deftest format-command-error-test
(let [table [{:cmds [] :doc "tool"}
Expand All @@ -1328,6 +1357,14 @@
"Commands:\n dev Start dev.\n deps Dep tools\n\n"
"Run \"tool --help\" for more information.")
s))))
(testing "no :prog"
(let [s (cli/format-command-error {:cause :no-match :wrong-input "nope"
:dispatch []
:tree (cli/table->tree table)})]
(is (= (str "Unknown command: nope\n\n"
"Commands:\n dev Start dev.\n deps Dep tools\n\n"
"Run \"--help\" for more information.")
s))))
(testing ":input-exhausted (incomplete multi-word command) renders the group's commands"
(let [s (cli/format-command-error {:cause :input-exhausted
:dispatch ["deps"] :prog "tool"
Expand Down Expand Up @@ -1396,6 +1433,7 @@
(testing ":help true works (no :prog; usage line omits it)"
(let [{:keys [out exit]} (run-dispatch [{:cmds [] :doc "t"} {:cmds ["go"] :fn identity :doc "Go"}]
["--help"] {:help true})]
(is (str/includes? out "Usage: [options] <command>"))
(is (str/includes? out "Commands:"))
(is (nil? exit))))
(testing "*exit-fn* codes can be remapped by :cause (e.g. group -> 0)"
Expand Down
Loading