Skip to content
Open
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
63 changes: 61 additions & 2 deletions src/spectrecoff-cli/commands/Prompt.fs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ type PromptExample() =
override _.Execute(_context, _) =

let fruits = ["Kiwi"; "Pear"; "Grape"; "Plum"; "Banana" ; "Orange"; "Durian"]
let chosenFruit = "If you had to pick one, which would it be?" |> chooseFrom fruits
let chosenFruits = "Which all do you actually like?" |> chooseMultipleFrom fruits
let chosenFruit = "If you had to pick one, which would it be?" |> choose fruits
let chosenFruits = "Which all do you actually like?" |> chooseMultiple fruits

match chosenFruits.Count with
| 0 -> "You don't like any fruit??"
Expand Down Expand Up @@ -43,4 +43,63 @@ type PromptExample() =
| true -> Pumped "Bon apetit!"
| false -> Edgy "Ok, maybe later :/"
|> toConsole

// example for chooseFromValues
let choices = [
{ Value = 42; Label = "the answer" }
{ Value = 13; Label = "some say it's unlucky" }
]

let chosenValue = chooseWithLabel choices "Which number do you like best from its description?"
$"I like {chosenValue}, too"
|> printMarkedUp
0

type PromptDocumentation() =
inherit Command<PromptSettings>()
interface ICommandLimiter<PromptSettings>

override _.Execute(_context, _) =
applyTheme documentationTheme

BL |> toConsole
pumped "Prompt module"
|> alignedRule Left
|> toConsole

Many [
Many [
C "This module provides functionality from the prompts of Spectre.Console ("
Link "https://spectreconsole.net/prompts"
C ")"
]
Many [C "This module provides functionality from the"; E "prompt"; C "of Spectre.Console"]
BL
emptyRule
C "For prompting an answer from the user, the following functions can be used:"
BI [
P "ask<'T> = (question: string) -> 'T"
P "askWith<'T> = (options: PromptOptions) (question: string) -> 'T"
P "askSuggesting<'T> = (answer: 'T) (question: string) -> 'T"
P "askWithSuggesting<'T> = (options: PromptOptions) (answer: 'T) (question: string) -> 'T"
]
Many [C "Here,"; P "PromptOptions"; C "is a record with two boolean properties:"]
BI [
Many [P "Secret"; C "describes whether the characters are shown (default: false)"]
Many [P "Optional"; C "describes whether empty is a valid input (default: false)"]
]
BL
emptyRule
C "If the set of choices is finite, one of the following can be used:"
BI [
Many [P "choose = (choices: string list) (question: string) -> string"; C "a simple single selection prompt"]
Many [P "chooseWithLabel<'T> (choices: ChoiceWithLabel<'T> list) (question: string) -> string"; C "a single selection prompt that gives a typed response"]
Many [P "chooseMultiple = (choices: string list) (question: string) -> string list"; C "select multiple values from a list of choices"]
Many [P "chooseMultipleWith = (options: MultiSelectionPromptOptions) (choices: string list) (question: string) -> string list"; C " selecting multiple entries with configuration of the number of visible entries per page (default: 10)"]
]
BL
emptyRule
C "And finally, this function is suitable for a yes/no question:"
BI [ P "confirm = (question: string) -> bool" ]
] |> toConsole
0
95 changes: 95 additions & 0 deletions src/spectrecoff/Prompt.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
[<AutoOpen>]
module SpectreCoff.Prompt

open Spectre.Console

type PromptOptions =
{ Secret: bool
Optional: bool }

type MultiSelectionPromptOptions =
{ PageSize: int }

type Choice<'T> =
{
Label: string
Value: 'T
}

let defaultOptions = { Secret = false; Optional = false; }
let defaultMultiSelectionOptions = { PageSize = 10; }

[<RequireQualifiedAccess>]
module private Prompts =
let selectionPrompt question choices =
let prompt = SelectionPrompt()
prompt.AddChoices (choices |> Seq.toArray) |> ignore
prompt.Title <- question
prompt

let selectionPromptT<'T> question (choices: Choice<'T> list) =
let choiceValues = List.map (fun c -> c.Value) choices
let choiceToTextConverter = fun (v: 'T) -> c.Label
let prompt = SelectionPrompt<'T>()
prompt.AddChoices (choiceValues |> Seq.toArray) |> ignore
prompt.Title <- question
prompt.Converter <- choiceToTextConverter
prompt

let multiSelectionPrompt question choices (options: MultiSelectionPromptOptions) =
let prompt = MultiSelectionPrompt()
prompt.AddChoices (choices |> Seq.toArray) |> ignore
prompt.Title <- question
prompt.PageSize <- options.PageSize
prompt

let textPrompt<'T> question (options: PromptOptions) =
let prompt = TextPrompt<'T> question
prompt.IsSecret <- options.Secret
prompt.AllowEmpty <- options.Optional
prompt

let textPromptWithDefault<'T> question (answer: 'T) (options: PromptOptions) =
let prompt = textPrompt<'T> question options
prompt.DefaultValue answer

let private prompt prompter =
AnsiConsole.Prompt prompter;

let choose (choices: string list) question =
prompt (Prompts.selectionPrompt question choices)

let chooseWithLabel<'T when 'T: equality> (choices: ChoiceWithLabel<'T> list ) question =
let choiceLabelMap =
choices
|> Seq.map (fun choice -> choice.Value, choice.Label)
|> dict
let converter = (fun choice -> choiceLabelMap[choice])

choiceLabelMap.Keys
|> Prompts.selectionPromptT converter question
|> prompt

let chooseFromValues2<'T> (choices: Choice<'T> list) (question: string): Choice<'T> =
prompt (Prompts.selectionPromptT converter question values)

let chooseMultipleFromWith options (choices: string list) question =
prompt (Prompts.multiSelectionPrompt question choices options)

let chooseMultiple =
chooseMultipleWith defaultMultiSelectionOptions

let ask<'T> question =
prompt (Prompts.textPrompt<'T> question defaultOptions)

let askWith<'T> options question =
prompt (Prompts.textPrompt<'T> question options)

let askSuggesting<'T> answer question =
prompt (Prompts.textPromptWithDefault<'T> question answer defaultOptions)

let askWithSuggesting<'T> options answer question =
prompt (Prompts.textPromptWithDefault<'T> question answer options)

let confirm question =
AnsiConsole.Confirm question
39 changes: 30 additions & 9 deletions src/spectrecoff/Spectre/Prompt.fs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ type PromptOptions =
type MultiSelectionPromptOptions =
{ PageSize: int }

let mutable defaultOptions =
{ Secret = false
Optional = false }
let mutable defaultMultiSelectionOptions =
{ PageSize = 10 }
type ChoiceWithLabel<'T> =
{
Label: string
Value: 'T
}

let defaultOptions = { Secret = false; Optional = false; }
let defaultMultiSelectionOptions = { PageSize = 10; }

[<RequireQualifiedAccess>]
module private Prompts =
Expand All @@ -24,6 +27,13 @@ module private Prompts =
prompt.Title <- question
prompt

let selectionPromptT<'T> converter question values =
let prompt = SelectionPrompt<'T>()
prompt.AddChoices (values |> Seq.toArray) |> ignore
prompt.Title <- question
prompt.Converter <- converter
prompt

let multiSelectionPrompt question choices (options: MultiSelectionPromptOptions) =
let prompt = MultiSelectionPrompt()
prompt.AddChoices (choices |> Seq.toArray) |> ignore
Expand All @@ -44,14 +54,25 @@ module private Prompts =
let private prompt prompter =
AnsiConsole.Prompt prompter;

let chooseFrom (choices: string list) question =
let choose (choices: string list) question =
prompt (Prompts.selectionPrompt question choices)

let chooseMultipleFromWith options (choices: string list) question =
let chooseWithLabel<'T when 'T: equality> (choices: ChoiceWithLabel<'T> list ) question =
let choiceLabelMap =
choices
|> Seq.map (fun choice -> choice.Value, choice.Label)
|> dict
let converter = (fun choice -> choiceLabelMap[choice])

choiceLabelMap.Keys
|> Prompts.selectionPromptT converter question
|> prompt

let chooseMultipleWith options (choices: string list) question =
prompt (Prompts.multiSelectionPrompt question choices options)

let chooseMultipleFrom =
chooseMultipleFromWith defaultMultiSelectionOptions
let chooseMultiple =
chooseMultipleWith defaultMultiSelectionOptions

let ask<'T> question =
prompt (Prompts.textPrompt<'T> question defaultOptions)
Expand Down