1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188[<AutoOpen>]
module Duets.Cli.Components.ChoicePrompt
open Duets.Common
open Spectre.Console
open Duets.Cli.Text
let private showSelection choice =
showSeparator None
Generic.choiceSelection choice |> showMessage
showSeparator None
let private createChoicePrompt title optionTextFn (choices: 'a seq) =
let mutable selectionPrompt = SelectionPrompt<'a>()
selectionPrompt.Title <- title
selectionPrompt <- selectionPrompt.AddChoices(choices)
selectionPrompt <- selectionPrompt.UseConverter(fun c -> optionTextFn c)
selectionPrompt
/// <summary>
/// Shows the user a list of options to choose from, forcing them to choose one
/// in order to continue.
/// </summary>
/// <param name="title">Title of the prompt</param>
/// <param name="optionTextFn">
/// Function to transform an item from the choice sequence into text
/// </param>
/// <param name="choices">Sequence of options to show to the user</param>
/// <returns>The selected item</returns>
let showChoicePrompt<'a> title optionTextFn (choices: 'a seq) =
createChoicePrompt title optionTextFn choices
|> AnsiConsole.Prompt
|> Pipe.tap (optionTextFn >> showSelection)
/// <summary>
/// Shows the user a list of options to choose from, with a text field to
/// be able to search. Forcing them to choose one in order to continue.
/// </summary>
/// <param name="title">Title of the prompt</param>
/// <param name="optionTextFn">
/// Function to transform an item from the choice sequence into text
/// </param>
/// <param name="choices">Sequence of options to show to the user</param>
/// <returns>The selected item</returns>
let showSearchableChoicePrompt<'a> title optionTextFn (choices: 'a seq) =
let mutable selectionPrompt = createChoicePrompt title optionTextFn choices
selectionPrompt.SearchEnabled <- true
selectionPrompt
|> AnsiConsole.Prompt
|> Pipe.tap (optionTextFn >> showSelection)
let private optionalTextFn optionTextFn backText opt =
match opt with
| Some item -> optionTextFn item
| None -> backText
let private createOptionalChoicePrompt title backText optionTextFn choices =
choices
|> Seq.map Some
|> fun options -> Seq.append options [ None ]
|> createChoicePrompt title (optionalTextFn optionTextFn backText)
/// <summary>
/// Shows the user a list of options to choose from, with one extra option that
/// acts as an alternative choice to not select any of the items.
/// </summary>
/// <param name="title">Title of the prompt</param>
/// <param name="backText">
/// Text for the <i>cancel</i> or <i>back</i> option
/// </param>
/// <param name="optionTextFn">
/// Function to transform an item from the choice sequence into text
/// </param>
/// <param name="choices">Sequence of options to show to the user</param>
/// <returns>
/// An item wrapped in <i>Some</i> if the user selected an item or <i>None</i>
/// if they selected the <i>cancel</i> option.
/// </returns>
let showOptionalChoicePrompt title backText optionTextFn choices =
createOptionalChoicePrompt title backText optionTextFn choices
|> AnsiConsole.Prompt
|> Pipe.tap (optionalTextFn optionTextFn backText >> showSelection)
/// <summary>
/// Shows the user a list of options to choose from, with the option to cancel
/// the selection by pressing Esc.
/// </summary>
/// <param name="title">Title of the prompt</param>
/// <param name="cancelOptionText">
/// Text for the <i>cancel</i> or <i>back</i> option
/// </param>
/// <param name="optionTextFn">
/// Function to transform an item from the choice sequence into text
/// </param>
/// <param name="choices">Sequence of options to show to the user</param>
/// <returns>
/// An item wrapped in <i>Some</i> if the user selected an item or <i>None</i>
/// if they selected the <i>cancel</i> option.
/// </returns>
let showCancellableChoicePrompt
title
cancelOptionText
optionTextFn
(choices: 'a seq)
=
let mutable selectionPrompt = createChoicePrompt title optionTextFn choices
selectionPrompt.AbortPlaceholderText <-
$"Press Esc to {cancelOptionText}" |> Styles.faded
match AnsiConsole.TryPrompt(selectionPrompt) with
| true, item ->
item |> optionTextFn |> showSelection
Some item
| false, _ ->
showSelection cancelOptionText
None
/// <summary>
/// Shows the user a list of options to choose from, with the option to cancel
/// the selection by pressing Esc.
/// </summary>
/// <param name="title">Title of the prompt</param>
/// <param name="cancelOptionText">
/// Text for the <i>cancel</i> or <i>back</i> option
/// </param>
/// <param name="optionTextFn">
/// Function to transform an item from the choice sequence into text
/// </param>
/// <param name="choices">Sequence of options to show to the user</param>
/// <returns>
/// An item wrapped in <i>Some</i> if the user selected an item or <i>None</i>
/// if they selected the <i>cancel</i> option.
/// </returns>
let showSearchableOptionalChoicePrompt
title
cancelOptionText
optionTextFn
(choices: 'a seq)
=
let mutable selectionPrompt = createChoicePrompt title optionTextFn choices
selectionPrompt.SearchEnabled <- true
selectionPrompt.AbortPlaceholderText <-
$"Press Esc to {cancelOptionText}" |> Styles.faded
match AnsiConsole.TryPrompt(selectionPrompt) with
| true, item ->
item |> optionTextFn |> showSelection
Some item
| false, _ ->
showSelection cancelOptionText
None
/// <summary>
/// Shows the user a list of options to choose from, allowing them to choose
/// one or multiple items.
/// </summary>
/// <param name="title">Title of the prompt</param>
/// <param name="optionTextFn">
/// Function to transform an item from the choice sequence into text
/// </param>
/// <param name="choices">Sequence of options to show to the user</param>
/// <returns>The selected item(s)</returns>
let showMultiChoicePrompt<'a> title optionTextFn (choices: 'a seq) =
let mutable multiSelectionPrompt = MultiSelectionPrompt<'a>()
multiSelectionPrompt.Title <- title
multiSelectionPrompt.MoreChoicesText <- Generic.multiChoiceMoreChoices
multiSelectionPrompt.InstructionsText <- Generic.multiChoiceInstructions
multiSelectionPrompt.Required <- true
multiSelectionPrompt.PageSize <- 10
multiSelectionPrompt <- multiSelectionPrompt.AddChoices(choices)
multiSelectionPrompt <-
multiSelectionPrompt.UseConverter(fun c -> optionTextFn c)
AnsiConsole.Prompt(multiSelectionPrompt) |> List.ofSeq