Skip to content

Commit ee618bc

Browse files
committed
Use gum and fzf as optional prompt interfaces
1 parent cd236ff commit ee618bc

File tree

5 files changed

+180
-19
lines changed

5 files changed

+180
-19
lines changed

src/k16/kl/commands/containers.clj

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
(ns k16.kl.commands.containers
22
(:require
3+
[cli-matic.utils :as cli.utils]
34
[clojure.pprint :as pprint]
45
[k16.kl.api.executor :as api.executor]
56
[k16.kl.api.module :as api.module]
67
[k16.kl.api.proxy :as api.proxy]
78
[k16.kl.api.resolver :as api.resolver]
89
[k16.kl.api.state :as api.state]
10+
[k16.kl.prompt :as prompt]
911
[k16.kl.prompt.config :as prompt.config]
10-
[meta-merge.core :as metamerge]
11-
[pretty.cli.prompt :as prompt]))
12+
[meta-merge.core :as metamerge]))
1213

1314
(set! *warn-on-reflection* true)
1415

@@ -58,8 +59,12 @@
5859
{:value (name container-name)
5960
:label (name container-name)
6061
:checked (get-in state [:containers container-name :enabled] true)})))
61-
selected-containers (->> options
62-
(prompt/list-checkbox "Select Services")
62+
63+
selection-result (prompt/select-multi "Select Services" options)
64+
_ (when-not selection-result
65+
(cli.utils/exit! "Cancelled" 1))
66+
67+
selected-containers (->> selection-result
6368
(map keyword)
6469
set)
6570

src/k16/kl/commands/services.clj

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
(ns k16.kl.commands.services
22
(:require
3+
[cli-matic.utils :as cli.utils]
34
[clojure.pprint :as pprint]
45
[k16.kl.api.module :as api.module]
56
[k16.kl.api.proxy :as api.proxy]
67
[k16.kl.api.resolver :as api.resolver]
78
[k16.kl.api.state :as api.state]
9+
[k16.kl.prompt :as prompt]
810
[k16.kl.prompt.config :as prompt.config]
9-
[meta-merge.core :as metamerge]
10-
[pretty.cli.prompt :as prompt]))
11+
[meta-merge.core :as metamerge]))
1112

1213
(defn- set-default-service-endpoint! [props]
1314
(let [module-name (prompt.config/get-module-name props)
@@ -18,23 +19,27 @@
1819
state (api.state/get-state module-name)
1920

2021
service-name
21-
(-> (prompt/list-select "Select Service"
22-
(->> (get-in module [:network :services])
23-
(map (fn [[service-name]]
24-
{:value (name service-name)
25-
:label (name service-name)}))))
22+
(-> (prompt/select "Select Service"
23+
(->> (get-in module [:network :services])
24+
(map (fn [[service-name]]
25+
{:value (name service-name)
26+
:label (name service-name)}))))
2627
keyword)
2728

29+
_ (when-not service-name (cli.utils/exit! "No service selected" 1))
30+
2831
service (get-in module [:network :services service-name])
2932

3033
endpoint-name
31-
(-> (prompt/list-select "Select Default Endpoint"
32-
(->> (:endpoints service)
33-
(map (fn [[endpoint-name]]
34-
{:value (name endpoint-name)
35-
:label (name endpoint-name)}))))
34+
(-> (prompt/select "Select Default Endpoint"
35+
(->> (:endpoints service)
36+
(map (fn [[endpoint-name]]
37+
{:value (name endpoint-name)
38+
:label (name endpoint-name)}))))
3639
keyword)
3740

41+
_ (when-not endpoint-name (cli.utils/exit! "No endpoint selected" 1))
42+
3843
updated-state
3944
(assoc-in state [:network :services service-name :default-endpoint]
4045
endpoint-name)]

src/k16/kl/prompt.clj

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
(ns k16.kl.prompt
2+
(:require
3+
[babashka.process :as proc]
4+
[clojure.string :as str]
5+
[k16.kl.prompt.proc :as prompt.proc]
6+
[pretty.cli.prompt :as prompt]))
7+
8+
(set! *warn-on-reflection* true)
9+
10+
(def ^:private -gum-installed?
11+
(delay
12+
(let [res (proc/sh ["which" "gum"])]
13+
(= 0 (:exit res)))))
14+
15+
(def ^:private -fzf-installed?
16+
(delay
17+
(let [res (proc/sh ["which" "fzf"])]
18+
(= 0 (:exit res)))))
19+
20+
(defn- select-with-gum [title options {:keys [multi-select?]}]
21+
(let [options
22+
(->> options
23+
(map (fn [option]
24+
(if (string? option)
25+
{:label option}
26+
option))))
27+
28+
selected
29+
(->> options
30+
(filter :checked)
31+
(map :label))
32+
33+
indexed
34+
(->> options
35+
(reduce (fn [acc option]
36+
(assoc acc (:label option) (:value option)))
37+
{}))
38+
39+
input
40+
(->> options
41+
(map :label)
42+
(str/join "\n"))]
43+
44+
(if multi-select?
45+
(let [command (concat ["gum" "choose" "--header" title]
46+
(map :label options)
47+
["--no-limit" "--selected" (str/join "," selected)])
48+
49+
{:keys [stdout code]}
50+
(prompt.proc/shell {:stdin input :cmd command})
51+
52+
results (str/split stdout #"\n")]
53+
54+
(if (= 0 code)
55+
(map (fn [result]
56+
(get indexed result))
57+
results)
58+
nil))
59+
60+
(let [command ["gum" "filter"]
61+
{:keys [code stdout]} (proc/shell {:stdin input :cmd command})]
62+
(if (= 0 code)
63+
(get indexed (str/trim stdout))
64+
nil)))))
65+
66+
(defn- select-with-fzf [title options]
67+
(let [options
68+
(->> options
69+
(map (fn [option]
70+
(if (string? option)
71+
{:label option :value option}
72+
option))))
73+
74+
indexed
75+
(->> options
76+
(reduce (fn [acc option]
77+
(assoc acc (:label option) (:value option)))
78+
{}))
79+
80+
input
81+
(->> options
82+
(map :label)
83+
(str/join "\n"))
84+
85+
{:keys [stdout]}
86+
(prompt.proc/shell {:cmd ["fzf" "--layout" "reverse" "--header" title "--height" (str (+ 3 (count options)))]
87+
:stdin input})]
88+
89+
(get indexed (str/trim-newline stdout))))
90+
91+
(defn select [title options]
92+
(cond
93+
@-fzf-installed?
94+
(select-with-fzf title options)
95+
96+
@-gum-installed?
97+
(select-with-gum title options {})
98+
99+
:else
100+
(prompt/list-select title options)))
101+
102+
(defn select-multi [title options]
103+
(cond
104+
@-gum-installed?
105+
(select-with-gum title options {:multi-select? true})
106+
107+
:else
108+
(prompt/list-checkbox title options)))

src/k16/kl/prompt/config.clj

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
(ns k16.kl.prompt.config
22
(:require
33
[k16.kl.api.fs :as api.fs]
4-
[pretty.cli.prompt :as prompt]))
4+
[k16.kl.prompt :as prompt]
5+
[cli-matic.utils :as cli.utils]))
56

67
(set! *warn-on-reflection* true)
78

@@ -12,5 +13,7 @@
1213

1314
(and default-module (not skip-default?)) default-module
1415

15-
:else (let [modules (api.fs/list-modules)]
16-
(prompt/list-select "Select Module" modules)))))
16+
:else (let [modules (api.fs/list-modules)
17+
module-name (prompt/select "Select Module" modules)]
18+
(when-not module-name (cli.utils/exit! "No endpoint selected" 1))
19+
module-name))))

src/k16/kl/prompt/proc.clj

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
(ns k16.kl.prompt.proc
2+
(:require
3+
[clojure.string :as str]
4+
[promesa.core :as p])
5+
(:import
6+
[java.io BufferedWriter InputStream OutputStreamWriter]
7+
[java.lang ProcessBuilder ProcessBuilder$Redirect]))
8+
9+
(set! *warn-on-reflection* true)
10+
11+
(defn- write-initial-input [output-stream ^String data]
12+
(with-open [writer (BufferedWriter. (OutputStreamWriter. output-stream))]
13+
(.write writer data)
14+
(.flush writer)))
15+
16+
(defn- read-process-output [^InputStream input-stream]
17+
(p/vthread
18+
(let [buffer (byte-array 1024)]
19+
(loop [result (transient [])]
20+
(let [n-read (.read input-stream buffer)]
21+
22+
(if (> n-read -1)
23+
(let [chunk (subvec (vec buffer) 0 n-read)]
24+
(recur (conj! result (String. (byte-array chunk)))))
25+
26+
(str/join (persistent! result))))))))
27+
28+
(defn shell [{:keys [stdin cmd]}]
29+
(let [process-builder (ProcessBuilder. ^java.util.List cmd)]
30+
31+
(.redirectInput process-builder ProcessBuilder$Redirect/PIPE)
32+
(.redirectError process-builder ProcessBuilder$Redirect/INHERIT)
33+
34+
(let [process (.start process-builder)
35+
stdout-promise (read-process-output (.getInputStream process))]
36+
37+
(write-initial-input (.getOutputStream process) stdin)
38+
39+
{:code (.waitFor process)
40+
:stdout @stdout-promise})))

0 commit comments

Comments
 (0)