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
2 changes: 1 addition & 1 deletion .github/workflows/clojure.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:

steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6

- name: Setup Java
uses: actions/setup-java@v5
Expand Down
4 changes: 2 additions & 2 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
* You may now enter `-h` or `--help` after a group to get help for just that group
* Tool help output has been reordered, with top-level tool commands first (previously, those were in a "Builtin" group and listed last)
* Tool help now displays just root-level commands by default (add `--commands all` to list nested commands)
* When extracting the first sentence as the single-line index, embedded periods are no longer considered the end of the sentence
* When extracting the first sentence as the single-line title, embedded periods are no longer considered the end of the sentence
* `net.lewisship.cli-tools`:
* Added function `tool-name`
* Added function `command-root`
Expand All @@ -38,7 +38,7 @@
* :source-dirs specifies extra directories to consider when caching
* :pre-dispatch - callback function invoked before dispatch
* :pre-invoke - callback function invoked before the dispatched command function is invoked
* Can now handle "messy" case where a command has the same name as a group
* Can now handle the case where a command has the same name as a group
* Cache files are now stored in `~/.cache/net.lewisship.cli-tools` by default
* Added initial support for commands defined as Babashka CLI functions
* Added `net.lewiship.cli-tools.test` namespace
Expand Down
4 changes: 2 additions & 2 deletions deps.edn
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
{:extra-deps {org.clojure/tools.cli {:mvn/version "1.2.245" :optional true}
babashka/fs {:mvn/version "0.5.27" :optional true}
babashka/process {:mvn/version "0.6.23" :optional true}
selmer/selmer {:mvn/version "1.12.65" :optional true}
org.babashka/cli {:mvn/version "0.8.66" :optional true}}}
selmer/selmer {:mvn/version "1.12.69" :optional true}
org.babashka/cli {:mvn/version "0.8.67" :optional true}}}

:1.11
{:override-deps {org.clojure/clojure ^:antq/exclude {:mvn/version "1.11.4"}}}
Expand Down
4 changes: 1 addition & 3 deletions resources/net/lewisship/cli_tools/group.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
case "$state" in
cmds)
_values "{{tool}} {{group.name}} subcommands" {% for sub in group.subs %} \
"{{sub.name}}[{{sub.summary}}]" {% endfor %}
"{{sub.name}}[{{sub.title}}]" {% endfor %}
;;
args)
case $line[1] in {% for sub in group.subs %}
Expand All @@ -17,5 +17,3 @@
;;
esac
}


8 changes: 4 additions & 4 deletions resources/net/lewisship/cli_tools/top-level.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
_{{tool}}() {
local line state

_arguments -C \
_arguments -C {% for opt in options %} \
{{ opt }} {% endfor %} \
"1: :->cmds" \
"*::arg:->args"
"*::args:->args"

case "$state" in
cmds)
_values "{{tool}} command" {% for cmd in commands %} \
"{{cmd.name}}[{{cmd.summary}}]" {% endfor %}
"{{cmd.name}}[{{cmd.title}}]" {% endfor %}
;;
args)
case $line[1] in {% for cmd in commands %}
Expand All @@ -20,4 +21,3 @@ _{{tool}}() {
;;
esac
}

14 changes: 3 additions & 11 deletions src/net/lewisship/cli_tools.clj
Original file line number Diff line number Diff line change
Expand Up @@ -190,14 +190,6 @@
~validations)
~@body))))))


(def ^:private
default-tool-options
"Default tool command line options."
[["-C" "--color" "Enable ANSI color output"]
["-N" "--no-color" "Disable ANSI color output"]
["-h" "--help" "This command summary"]])

(defn- expand-tool-options
"Expand dispatch options into tool options, leveraging a cache."
[options]
Expand All @@ -221,7 +213,7 @@
(merge {:tool-name tool-name'
:cache-digest digest
:command-root command-root}
(select-keys options [:doc :arguments :tool-summary :pre-dispatch :pre-invoke]))))
(select-keys options [:doc :arguments :tool-summary :pre-dispatch :pre-invoke :extra-tool-options]))))

(defn- dispatch*
"Called (indirectly/anonymously) from a tool handler to process remaining command line arguments."
Expand Down Expand Up @@ -308,7 +300,7 @@
default-dispatch-options
dispatch-options)
{:keys [extra-tool-options tool-options-handler]} merged-options
full-options (concat extra-tool-options default-tool-options)
full-options (concat extra-tool-options impl/default-tool-options)
{:keys [options arguments summary errors]}
(cli/parse-opts (:arguments merged-options)
full-options
Expand All @@ -334,7 +326,7 @@

(if tool-options-handler
(tool-options-handler options dispatch-options' callback)
(dispatch* dispatch-options'))))))
(callback))))))

(defn select-option
"Builds a standard option spec for selecting from a list of possible values.
Expand Down
20 changes: 11 additions & 9 deletions src/net/lewisship/cli_tools/completions.clj
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,12 @@
(if fn
{:name command-name
:fn-name fn-name
:summary title
:title title
:options (options command-map)}
{:name (->> command-map
:command-path
(string/join " "))
:summary title
:title title
:fn-name fn-name
:subs (map #(extract-command fn-name %) (:subs command-map))})))

Expand All @@ -86,12 +86,14 @@
:command command}))))

(defn- print-tool
[tool-name command-root _groups]
[tool-name command-root extra-options]
(let [prefix (str "_" tool-name)
options (map #(apply to-opt %) (concat extra-options impl/default-tool-options))
commands (->> command-root
(keep #(extract-command prefix %)))]
(selmer.util/without-escaping
(render "top-level" {:tool tool-name
:options options
:commands commands})
(render-commands tool-name commands))))

Expand All @@ -102,23 +104,23 @@
output-path ["PATH" "File to write completions to."
:optional true]]
(binding [impl/*introspection-mode* true]
(let [{:keys [command-root tool-name groups]} impl/*tool-options*]
(let [{:keys [command-root tool-name extra-tool-options]} impl/*tool-options*
generator #(binding [ansi/*color-enabled* false]
(print-tool tool-name command-root extra-tool-options))]
(if output-path
(do
(with-open [w (-> output-path
fs/file
io/output-stream
io/writer)]
(try
(binding [*out* w
ansi/*color-enabled* false]
(print-tool tool-name command-root groups))
(binding [*out* w]
(generator))
(catch Throwable t
(abort 1 [:red
(command-path) ": "
(or (ex-message t)
(class t))]))))
(perr [:cyan "Wrote " output-path]))
;; Just write to standard output
(binding [ansi/*color-enabled* false]
(print-tool tool-name command-root groups))))))
(generator)))))
6 changes: 6 additions & 0 deletions src/net/lewisship/cli_tools/impl.clj
Original file line number Diff line number Diff line change
Expand Up @@ -1061,3 +1061,9 @@
:subs)]
(cond->> root
transformer (transformer dispatch-options))))

(def default-tool-options
"Default tool command line options."
[["-C" "--color" "Enable ANSI color output"]
["-N" "--no-color" "Disable ANSI color output"]
["-h" "--help" "This command summary"]])
8 changes: 5 additions & 3 deletions test-resources/expected/messy-completions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
_messy() {
local line state

_arguments -C \
_arguments -C \
'(-C --color)'{-C,--color}$'[Enable ANSI color output]' \
'(-N --no-color)'{-N,--no-color}$'[Disable ANSI color output]' \
'(-h --help)'{-h,--help}$'[This command summary]' \
"1: :->cmds" \
"*::arg:->args"
"*::args:->args"

case "$state" in
cmds)
Expand All @@ -29,7 +32,6 @@ _messy() {
;;
esac
}

_messy_completions() {
_arguments -s \
'(-h --help)'{-h,--help}$'[This command summary]'
Expand Down
8 changes: 5 additions & 3 deletions test-resources/expected/simple-completions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
_simple() {
local line state

_arguments -C \
_arguments -C \
'(-C --color)'{-C,--color}$'[Enable ANSI color output]' \
'(-N --no-color)'{-N,--no-color}$'[Disable ANSI color output]' \
'(-h --help)'{-h,--help}$'[This command summary]' \
"1: :->cmds" \
"*::arg:->args"
"*::args:->args"

case "$state" in
cmds)
Expand All @@ -26,7 +29,6 @@ _simple() {
;;
esac
}

_simple_colors() {
_arguments -s \
'(-h --help)'{-h,--help}$'[This command summary]'
Expand Down
10 changes: 5 additions & 5 deletions test-resources/expected/subgroup-completions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
_subgroup() {
local line state

_arguments -C \
_arguments -C \
'(-C --color)'{-C,--color}$'[Enable ANSI color output]' \
'(-N --no-color)'{-N,--no-color}$'[Disable ANSI color output]' \
'(-h --help)'{-h,--help}$'[This command summary]' \
"1: :->cmds" \
"*::arg:->args"
"*::args:->args"

case "$state" in
cmds)
Expand All @@ -26,7 +29,6 @@ _subgroup() {
;;
esac
}

_subgroup_completions() {
_arguments -s \
'(-h --help)'{-h,--help}$'[This command summary]'
Expand Down Expand Up @@ -57,8 +59,6 @@ _subgroup_subgroup() {
;;
esac
}


_subgroup_subgroup_example() {
_arguments -s \
'(-v --verbose)'{-v,--verbose}$'[Extra output]' \
Expand Down
41 changes: 41 additions & 0 deletions test-resources/expected/tool-options.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#compdef _options options

_options() {
local line state

_arguments -C \
'(-d --debug)'{-d,--debug}$'[Enable debug mode]' \
'(-o --output-path)'{-o,--output-path}$'[Write output to file, not stdout]':FILE \
'(-C --color)'{-C,--color}$'[Enable ANSI color output]' \
'(-N --no-color)'{-N,--no-color}$'[Disable ANSI color output]' \
'(-h --help)'{-h,--help}$'[This command summary]' \
"1: :->cmds" \
"*::args:->args"

case "$state" in
cmds)
_values "options command" \
"completions[Generate zsh command completions]" \
"help[List available commands]"
;;
args)
case $line[1] in
completions) _options_completions ;;

help) _options_help ;;

esac
;;
esac
}
_options_completions() {
_arguments -s \
'(-h --help)'{-h,--help}$'[This command summary]'
}

_options_help() {
_arguments -s \
'(-c --commands)'{-c,--commands}$'[Print commands: all, none, root]':FILTER \
'(-h --help)'{-h,--help}$'[This command summary]'
}

10 changes: 9 additions & 1 deletion test/net/lewisship/cli_tools/completions_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,17 @@
;; where command name and group name collide
;; Not sure the current behavior is correct
(is (match? (expected "messy-completions.txt")
(dispatch
(dispatch
{:tool-name "messy"
:namespaces '[net.lewisship.cli-tools.completions
net.lewisship.messy-commands]
:groups {"messy" {:namespaces '[net.lewisship.messy]
:doc "Messy command and group at same time"}}}))))

(deftest tool-options
(is (match? (expected "tool-options.txt")
(dispatch
{:tool-name "options"
:namespaces '[net.lewisship.cli-tools.completions]
:extra-tool-options [["-d" "--debug" "Enable debug mode"]
["-o" "--output-path FILE" "Write output to file, not stdout"]]}))))
1 change: 1 addition & 0 deletions test/net/lewisship/cli_tools_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
:cache-dir nil}]
(f))))

#_{:clj-kondo/ignore [:unused-private-var]}
(defn- capture
"Used when output changes to capture new output (the embedded ANSI sequences are hard to work with."
([file result]
Expand Down