Skip to content

Commit dca214d

Browse files
committed
Add bash completion
1 parent 37314ce commit dca214d

3 files changed

Lines changed: 78 additions & 14 deletions

File tree

README.md

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -609,29 +609,34 @@ map they receive.
609609

610610
## Shell completion
611611

612-
There is experimental support for shell tab-completion for zsh. It is bare
613-
bones, it can complete subcommands and flags, and is aware of nested
612+
There is experimental support for shell tab-completion for zsh and bash. It is
613+
bare bones, it can complete subcommands and flags, and is aware of nested
614614
subcommands, but don't expect it to be perfect. There is no smart completion of
615-
flag values yes.
615+
flag values or positional arguments yet, and it's not yet pluggable (specify
616+
type/completions of values), but it's already helpful.
616617

617618
To get started run the hidden `__licli install-completions` subcommand. This
618-
will create `~/.zsh/completions/_licli`, and add a section to your `~/.zshrc` to
619-
autoload it and associate it with the current command.
619+
will detect your shell, and install the necessary bits, and associate our
620+
dynamic completion with your specific command. You can add `--zsh` or `--bash`
621+
to explictly choose which shell to configure.
620622

621623
e.g.
622624

623625
```
624626
bin/my_command __licli install-completions
625627
```
626628

627-
When calling from Clojure (not Babashka), pass the full path to the executable
628-
as an argument. You can easily use `realpath` for that.
629+
When calling from Clojure (not Babashka), we can't derive the script name
630+
explicitly. Pass the full path to the executable as an argument. You can easily
631+
use `realpath` for that.
629632

630633
```
631634
bin/my_command __licli install-completions `realpath bin/my_command`
632635
```
633636

634-
Other shells may come in the future. Contributions to the shell completion are welcome!
637+
Other shells and smarter completions may come in the future. The current
638+
implementation is so we have a starting point that can be improved.
639+
Contributions to the shell completion are welcome!
635640

636641
<!-- opencollective -->
637642
## Lambda Island Open Source
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Bash completion function for lambdaisland/cli
2+
3+
_licli() {
4+
local cur prev words cword
5+
_init_completion || return
6+
7+
# Get completion suggestions from the CLI tool
8+
local completion_output
9+
completion_output="$( ${words[0]} __licli completions -- "${words[@]}" 2>/dev/null )"
10+
11+
if [ -n "$completion_output" ]; then
12+
# Parse the completion output (format: "option:description" or just "option")
13+
local -a completions
14+
local -A descriptions
15+
while IFS= read -r line; do
16+
# Extract option and description
17+
local option="${line%%:*}"
18+
local description="${line#*:}"
19+
completions+=( "$option" )
20+
descriptions["$option"]="$description"
21+
done <<< "$completion_output"
22+
23+
# Generate completions with compgen
24+
COMPREPLY=($(compgen -W "${completions[*]}" -- "$cur"))
25+
26+
# Show descriptions when multiple options exist
27+
if [ ${#COMPREPLY[@]} -gt 1 ]; then
28+
echo
29+
for option in "${COMPREPLY[@]}"; do
30+
printf "%-25s %s\n" "$option" "${descriptions[$option]}"
31+
done
32+
# Re-print the current command line
33+
printf "\n%s" "${COMP_LINE@P}"
34+
fi
35+
fi
36+
}

src/lambdaisland/cli/completions.clj

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
(declare with-completions)
88

9-
(defn zsh-completions [cmdspec {:lambdaisland.cli/keys [argv] :as opts}]
9+
(defn shell-completions [cmdspec {:lambdaisland.cli/keys [argv] :as opts}]
1010
(let [cmdspec (reduce
1111
(fn [cmdspec arg]
1212
(let [commands (cmdspec/prepare-cmdpairs (:commands (with-completions cmdspec)))]
@@ -31,8 +31,8 @@
3131
zshrc-path (io/file home ".zshrc")
3232
zshrc (if (.exists zshrc-path) (slurp zshrc-path) "")]
3333
(when-not script-name
34-
(println "When running via clj/clojure (not babashka), you need to pass the fully qualified of the script as the first argument to __install_zsh_completions")
35-
(println "e.g. bin/my_script __install_zsh_completions /full/path/to/bin/my_script")
34+
(println "When running via clj/clojure (not babashka), you need to pass the fully qualified of the script as the first argument to install-completions")
35+
(println "e.g. bin/my_script __licli install-completions /full/path/to/bin/my_script")
3636
(System/exit -1))
3737
(when-not (.exists cdir) (.mkdirs cdir))
3838
(println "Creating" (str (io/file cdir "_licli")))
@@ -54,8 +54,31 @@
5454
(spit zshrc-path (str zshrc "\n" stanza)))))
5555

5656
(defn install-bash-completions [opts]
57-
(throw (ex-info {} "Not implemented"))
58-
)
57+
(let [home (System/getProperty "user.home")
58+
script-name (or (System/getProperty "babashka.file") (first (:lambdaisland.cli/argv opts)))
59+
bashrc-path (io/file home ".bashrc")
60+
bashrc (if (.exists bashrc-path) (slurp bashrc-path) "")]
61+
(when-not script-name
62+
(println "When running via clj/clojure (not babashka), you need to pass the fully qualified of the script as the first argument to install-completions")
63+
(println "e.g. bin/my_script __licli install-completions /full/path/to/bin/my_script")
64+
(System/exit -1))
65+
(let [base-name (last (str/split script-name #"/"))
66+
stanza (str
67+
(when-not (re-find #"(?m)^# lambdaisland.cli completions" bashrc)
68+
(str
69+
"\n# lambdaisland.cli completions\n"
70+
"if [ -f " (str (io/file home ".bash_completion.d/_licli.bash")) " ]; then\n"
71+
" source " (str (io/file home ".bash_completion.d/_licli.bash")) "\n"
72+
"fi\n"))
73+
(str "complete -F _licli " script-name " " "*/" base-name " " base-name "\n"))]
74+
(let [cdir (io/file home ".bash_completion.d")]
75+
(when-not (.exists cdir) (.mkdirs cdir))
76+
(println "Creating" (str (io/file cdir "_licli.bash")))
77+
(spit (io/file cdir "_licli.bash")
78+
(slurp (io/resource "lambdaisland/cli/_licli.bash"))))
79+
(println "Updating" (str bashrc-path) ", adding:")
80+
(println stanza)
81+
(spit bashrc-path (str bashrc "\n" stanza)))))
5982

6083
(defn install-completions
6184
"Do the necessary setup to get shell completions. Shell will be guessed from
@@ -81,5 +104,5 @@
81104
["completions" {:doc "Generate completions based on partially completed arguments
82105
83106
called by shell functions to do the actual completing."
84-
:command (partial zsh-completions cmdspec)}
107+
:command (partial shell-completions cmdspec)}
85108
"install-completions" #'install-completions]}]))

0 commit comments

Comments
 (0)