diff --git a/src/spectrecoff-cli/commands/Prompt.fs b/src/spectrecoff-cli/commands/Prompt.fs index 154901d..c1efe89 100644 --- a/src/spectrecoff-cli/commands/Prompt.fs +++ b/src/spectrecoff-cli/commands/Prompt.fs @@ -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??" @@ -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() + interface ICommandLimiter + + 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 \ No newline at end of file diff --git a/src/spectrecoff/Prompt.fs b/src/spectrecoff/Prompt.fs new file mode 100644 index 0000000..b9bb097 --- /dev/null +++ b/src/spectrecoff/Prompt.fs @@ -0,0 +1,95 @@ +[] +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; } + +[] +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 \ No newline at end of file diff --git a/src/spectrecoff/Spectre/Prompt.fs b/src/spectrecoff/Spectre/Prompt.fs index fbed71e..82edf9d 100644 --- a/src/spectrecoff/Spectre/Prompt.fs +++ b/src/spectrecoff/Spectre/Prompt.fs @@ -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; } [] module private Prompts = @@ -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 @@ -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)